Link to the code that reproduces this issue
https://github.com/maxijonson/nextjs-prerender-issue
To Reproduce
(Using App Router on next@16.3.0-canary.67)
-
Two dynamic segments where the parent segment generates its params from a layout:
// app/[lang]/layout.tsx
export function generateStaticParams() {
return [{ lang: "en" }, { lang: "fr" }];
}
export default function LangLayout({ children }: { children: React.ReactNode }) {
return children;
}
-
A nested [slug] page whose generateStaticParams returns a non-empty array for one
parent value and [] for another:
// app/[lang]/thing/[slug]/page.tsx
export function generateStaticParams({ params }: { params: { lang: string } }) {
return params.lang === "en"
? [
{ lang: params.lang, slug: "a" },
{ lang: params.lang, slug: "b" },
]
: [];
}
export default async function Page({ params }: { params: Promise<{ lang: string; slug: string }> }) {
const { lang, slug } = await params;
return (
<div>
{lang} {slug}
</div>
);
}
-
For contrast, a sibling that returns the same non-empty set, but for every parent (the positive control):
// app/[lang]/thing-control/[slug]/page.tsx
export function generateStaticParams({ params }: { params: { lang: string } }) {
return [
{ lang: params.lang, slug: "a" },
{ lang: params.lang, slug: "b" },
];
}
// same default Page as above
-
Build:
rm -rf .next && npx next build
Current vs. Expected behavior
Expected: /en/thing/a and /en/thing/b prerender (the en GSP returned them).
fr simply contributes no static children. This is the documented bottom-up/top-down
nested generateStaticParams behavior.
Actual: thing/[slug] prerenders zero pages — the valid en pages are dropped too.
The route is still printed as ● (SSG) but emits no HTML and has no entries in the
prerender manifest. The only difference from the control route is the fr return value
([] vs [{slug:"a"},{slug:"b"}]), yet that empty return poisons the entire route.
Build output:
├ ● /[lang]/thing-control/[slug]
│ ├ /en/thing-control/a
│ ├ /en/thing-control/b
│ ├ /fr/thing-control/a
│ └ /fr/thing-control/b
└ ● /[lang]/thing/[slug] <-- no child paths, 0 pages
Verification:
find .next/server/app -path '*/thing/*' -name '*.html' # bug route => empty
find .next/server/app -path '*thing-control*' -name '*.html' # control => 4 files
Reproduced across versions and bundlers
| Next |
Turbopack |
Webpack (next build --webpack) |
| 16.2.4 |
0 pages (bug) |
0 pages (bug) |
| 16.2.9 |
0 pages (bug) |
0 pages (bug) |
| 16.3.0-canary.67 |
0 pages (bug) |
— |
Not Turbopack-specific (webpack reproduces it), not fixed in the latest stable, and still
present on canary.
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 25.5.0 (macOS, arm64 / Apple Silicon)
Binaries:
Node: 24.x
npm: 11.x
Relevant Packages:
next: 16.2.4 (also reproduced on 16.2.9 and 16.3.0-canary.67)
react: 19.2.4
react-dom: 19.2.4
typescript: 5.9.3
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
Dynamic Routes
Which stage(s) are affected? (Select all that apply)
next build (local)
Additional context
Workaround
Returning a placeholder slug for the empty parent case works around the issue, but is not ideal. This is also the workaround documented for Cache Components when all param values are unknown. However, in this case, all params are known, they're just known to not exist under a subset of the parent segments.
export function generateStaticParams({ params }: { params: { lang: string } }) {
return params.lang === "en"
? [{ lang: params.lang, slug: "a" }, { lang: params.lang, slug: "b" }]
: [{ lang: params.lang, slug: "_" }]; // placeholder slug
}
└ /[lang]/thing/[slug]
├ ● /en/thing/a
├ ● /en/thing/b
└ ● /fr/thing/_
Link to the code that reproduces this issue
https://github.com/maxijonson/nextjs-prerender-issue
To Reproduce
(Using App Router on
next@16.3.0-canary.67)Two dynamic segments where the parent segment generates its params from a layout:
A nested
[slug]page whosegenerateStaticParamsreturns a non-empty array for oneparent value and
[]for another:For contrast, a sibling that returns the same non-empty set, but for every parent (the positive control):
Build:
rm -rf .next && npx next buildCurrent vs. Expected behavior
Expected:
/en/thing/aand/en/thing/bprerender (theenGSP returned them).frsimply contributes no static children. This is the documented bottom-up/top-downnested
generateStaticParamsbehavior.Actual:
thing/[slug]prerenders zero pages — the validenpages are dropped too.The route is still printed as
●(SSG) but emits no HTML and has no entries in theprerender manifest. The only difference from the control route is the
frreturn value(
[]vs[{slug:"a"},{slug:"b"}]), yet that empty return poisons the entire route.Build output:
Verification:
Reproduced across versions and bundlers
next build --webpack)Not Turbopack-specific (webpack reproduces it), not fixed in the latest stable, and still
present on canary.
Provide environment information
Which area(s) are affected? (Select all that apply)
Dynamic Routes
Which stage(s) are affected? (Select all that apply)
next build (local)
Additional context
Workaround
Returning a placeholder slug for the empty parent case works around the issue, but is not ideal. This is also the workaround documented for Cache Components when all param values are unknown. However, in this case, all params are known, they're just known to not exist under a subset of the parent segments.