Child `generateStaticParams` skips pre-rendering ALL static params when returning empty array (`[]`) for SOME parent params
@icyJoseph is already working on this.
Since Jun 25, 2026.
- Dominant language
- JavaScript
- Stars
- 142k
- Forks
- 32.5k
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 351
Description
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 whosegenerateStaticParamsreturns 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/_
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.