generateStaticParams and dynamicParams work together to control how dynamic routes are built. The first lists the paths to prerender at build time, and the second decides the fate of the paths that are not listed. Together they replace getStaticPaths from the Pages Router.
generateStaticParams basics
Export it from a page, layout, or route handler that contains dynamic segments. It returns an array of objects, and each object fills in one set of dynamic segments. During next build it runs before the pages are generated, and during next dev it runs when you navigate to the route.
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then((r) => r.json())
return posts.map((post: { slug: string }) => ({ slug: post.slug }))
}Each returned object becomes one static page, and the page below it receives params as a Promise and awaits it like any dynamic route. If you return nothing at all instead of an array, the route renders dynamically.
Return shapes
The property names must match the segment names, and the value type depends on the route.
| Route | Return type |
|---|---|
| /product/[id] | { id: string }[] |
| /products/[category]/[product] | { category: string, product: string }[] |
| /products/[...slug] | { slug: string[] }[] |
Catch-all segments take an array, which is why the slug value above is a string array. For a route with multiple segments, generateStaticParams can return both at once, or the child can read the parent params through its options argument. You can also place it on a layout to generate the parent segments and let the child generate its own.
There is one direction rule that catches people out: a segment can generate params for itself and for segments above it, but never below. A layout at app/products/[category] can only generate the category, while the page at app/products/[category]/[product] can generate both.
dynamicParams
dynamicParams controls segments that were not generated. By default it is true, and unlisted URLs are generated at request time. Set it to false to return 404 for anything not in the list.
// app/blog/[slug]/page.tsx
export const dynamicParams = false
export async function generateStaticParams() {
return [{ slug: 'hello' }]
}Now only /blog/hello exists, and any other slug returns 404. With the default true, an unlisted path is generated on its first visit and then cached.
One mapping is worth getting right. dynamicParams replaces the whole fallback option from getStaticPaths, not just one of its values, so true covers the render-on-demand behavior and false covers the 404 behavior.
Under Cache Components
With Cache Components enabled, generateStaticParams must return at least one param, because an empty array fails the build. The error is a guard. Next.js runs the route with each sample param at build time to check that it does not reach for cookies, headers, or search params without a boundary, and with no params there is nothing to run.
That validation only covers the branches your samples actually reach. A page that takes a different path for some slugs, such as reading a cookie for private posts, is not checked at build time and will fail on the first real request instead.
dynamicParams is not available in this model, since unknown routes are served an App Shell and upgraded in the background instead of being 404ed. For that flow, see ISR with Cache Components. For the other export options that control rendering, see route segment config explained.
Rune AI
Key Insights
- generateStaticParams returns an array of param objects to prerender.
- Each object fills one set of dynamic segments for one route.
- dynamicParams true generates unlisted paths at request time.
- dynamicParams false returns 404 for unlisted paths.
- With Cache Components, generateStaticParams must return at least one param.
Frequently Asked Questions
What happens to a URL that generateStaticParams did not list?
Can generateStaticParams return an empty array?
Conclusion
generateStaticParams lists the param values to prerender at build time, and dynamicParams decides the fate of the paths that are not listed. Together they replace getStaticPaths from the Pages Router.
More in this topic
`generateMetadata` Explained with Real Examples
What generateMetadata does, when it runs, and how to use it for real routes: awaited params, deduplicated data fetching, extending parent metadata, and returning a 404 from metadata.
Canonical URLs in Next.js: `metadataBase`, `alternates.canonical`, and Dynamic Pages
How canonical URLs work in the Next.js App Router: setting metadataBase once, writing alternates.canonical per route, handling dynamic segments, and what happens when the base URL is missing.
Open Graph and Twitter Card Metadata in Next.js
How to write Open Graph and Twitter card metadata in the Next.js App Router: the openGraph and twitter fields, automatic card defaults, article tags, and image merge rules.