vercel / vercel/next.js

Child `generateStaticParams` skips pre-rendering ALL static params when returning empty array (`[]`) for SOME parent params

Open
#95,166 7 comments 3 reactions 1 assignee View on GitHub

@icyJoseph is already working on this.

Since Jun 25, 2026.

Dynamic Routes
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)

  1. 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;
    }
    
  2. 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>
      );
    }
    
  3. 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
    
  4. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.