Skip to content
alex@galhardo:~

$ cd ../blog

Keeping every page static in Next.js 16

· 1 min read

This site has three languages and not one page is rendered on request. Here is the setup, and the two mistakes that quietly turn a static page into a dynamic one.

Pipeline from MDX file to CDN

Tell the framework every param up front

A dynamic segment such as [locale] or [slug] is static only when the build knows all of its values.

tsx
// app/[locale]/blog/[slug]/page.tsx
export const dynamicParams = false
 
export function generateStaticParams() {
	return locales.flatMap((locale) => getPosts(locale).map(({ slug }) => ({ locale, slug })))
}

dynamicParams = false matters: without it, an unknown slug is rendered on demand instead of returning a 404.

Do not read the request

Reading cookies(), headers() or searchParams in a server component opts the whole route out of static generation. The usual offender in a multilingual site is locale detection.

ApproachResult
Redirect by Accept-Language in a proxyA function runs on every request
Locale in the URL onlyFully static

English without a prefix, still static

The default language has no /en prefix. Instead of a proxy function, a rewrite in next.config.ts maps the unprefixed path onto the [locale] segment. Rewrites are resolved by the CDN router.

ts
async rewrites() {
	return {
		afterFiles: [
			{ source: '/', destination: '/en' },
			{ source: '/:path((?!en|pt-br|es|admin|api|_next).*)', destination: '/en/:path' },
		],
	}
}

Check the build output

The build prints a table. Every public route should show ● (SSG) or ○ (static), never ƒ.

text
├ ● /[locale]/blog/[slug]
│ ├ /en/blog/hello-world
│ └ /pt-br/blog/hello-world

If a route shows ƒ, something in its tree read the request.