generateStaticParams and Pre-rendering Dynamic Routes

Intermediate
11 min

generateStaticParams and Pre-rendering Dynamic Routes

A dynamic route such as app/blog/[slug] cannot be pre-rendered unless Next.js knows which slugs exist. generateStaticParams provides that list, turning thousands of database rows into static HTML at build time while still handling new entries on demand. After this lesson you will be able to pre-render dynamic routes, decide what happens for unknown parameters, and combine static generation with revalidation.

Why Dynamic Routes Are Dynamic by Default

Without extra information, app/blog/[slug]/page.tsx must be rendered per request because the set of valid slugs is unknown at build time. That costs a server render on every visit and shows as ƒ (Dynamic) in the build output. Exporting generateStaticParams from the same file changes the picture: Next.js calls it during next build, renders one page per returned object, and marks the route ● (SSG).

Returning Parameters

The function returns an array of objects whose keys match the dynamic segments:

tsx
// app/products/[id]/page.tsx export async function generateStaticParams() { const res = await fetch("https://api.example.com/products"); const products: { id: number }[] = await res.json(); return products.map((p) => ({ id: String(p.id) })); }

Values must be strings, because they represent URL segments. Fetch calls made here are memoized, so if the page itself requests the same URL the data is not fetched twice. For a catch-all segment [...slug] return arrays: { slug: ["docs", "getting-started"] } produces /docs/getting-started.

Handling Unknown Parameters

What happens when a visitor requests /blog/new-post that did not exist at build time? The dynamicParams segment config decides:

| dynamicParams | Behaviour for a slug not in the list | |---|---| | true (default) | Rendered on demand, then cached like the pre-built pages | | false | Responds with 404 |

tsx
export const dynamicParams = false; // only pre-built slugs are valid

Keep the default when content is added after deployment (blog posts, products). Set it to false for closed sets such as [locale] or a fixed list of categories, where an unexpected value should be an error.

Combining with Revalidation

Pre-built pages can still refresh. Add a revalidation window and the route behaves like ISR: built once, regenerated in the background after the window expires, and new slugs rendered on first visit:

tsx
export const revalidate = 600; // seconds export async function generateStaticParams() { const posts = await getAllPosts(); return posts.slice(0, 50).map((post) => ({ slug: post.slug })); // pre-build the popular ones }

Pre-building only a subset is a common trade-off: build time stays short, the most visited pages are instant, and the long tail is generated lazily. With Cache Components enabled, tag the underlying data with cacheTag("posts") so an editor's Server Action can purge every affected page at once.

Nested Dynamic Segments

For app/[category]/[product]/page.tsx, the parent segment's generateStaticParams runs first and each child call receives the parent's params:

tsx
// app/[category]/[product]/page.tsx export async function generateStaticParams({ params }: { params: { category: string } }) { const products = await getProducts(params.category); return products.map((p) => ({ product: p.slug })); }

Alternatively, generate both keys from the child in one pass by returning { category, product } objects. Choose the single-pass form when one query already gives you every combination.

Common mistakes

  • Returning numbers instead of strings; the build fails with a type error.
  • Reading cookies() or headers() inside the page, which makes the route dynamic regardless of generateStaticParams.
  • Forgetting that generateStaticParams runs only at build time; in next dev pages are rendered on request.
Quick Quiz
Question 1 of 3

What does `generateStaticParams` return?

Key Takeaways

  • generateStaticParams lists the parameter combinations to pre-render at build time.
  • Return string values, and arrays for catch-all segments; keys match the [param] folder names.
  • dynamicParams controls whether unknown parameters render on demand (default) or 404.
  • Add revalidate to refresh pre-built pages, and pre-build only the pages that matter most.
  • Nested segments receive parent params, or generate all keys at once from the deepest segment.

Next lesson: Parallel and Intercepting Routes — render several pages in one layout and open routes as modals.

generateStaticParams and Pre-rendering Dynamic Routes - Next.js | CodeYourCraft | CodeYourCraft