The next.js generateStaticParams function tells Next.js which values of a dynamic route segment to render ahead of time at build, instead of rendering them the first time a visitor requests that URL. Export it alongside a page that uses a dynamic segment.
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await fetch("https://api.example.com/posts").then((res) =>
res.json()
);
return posts.map((post: { slug: string }) => ({ slug: post.slug }));
}Each object in the returned array becomes one page built at compile time. Returning ten post slugs here builds ten static blog post pages before the app ever receives a real visitor.
The return shape
The function must return an array of objects, and each object's keys must match the dynamic segment names in the route's file path exactly.
| Route | Return type |
|---|---|
app/product/[id]/page.tsx | { id: string }[] |
app/products/[category]/[product]/page.tsx | { category: string; product: string }[] |
app/product/[...slug]/page.tsx | { slug: string[] }[] |
A single dynamic segment needs one key per object; a catch-all segment needs an array of strings as its value, matching the same shape the page's own params prop expects.
Multiple dynamic segments
For a route with more than one dynamic segment, a single function at the page level can generate every value at once.
// app/products/[category]/[product]/page.tsx
export async function generateStaticParams() {
const products = await fetch("https://api.example.com/products").then(
(res) => res.json()
);
return products.map((p: { category: string; id: string }) => ({
category: p.category,
product: p.id,
}));
}A layout above the page can only generate the segments up to its own level, since it has no visibility into segments defined deeper in the tree. A nested version of this function in a child segment also receives the parent's resolved value as an argument, which is useful for fetching only the products inside one already-known category rather than refetching the full catalog at every level.
This bottom-up approach, generating every segment from the deepest page, is usually the simpler starting point. Reach for the parent-to-child version only once a route genuinely benefits from splitting one large fetch into smaller, scoped ones per level.
What happens to unlisted values
By default, a value this function does not return still renders normally the first time someone requests it, rather than failing. Setting dynamicParams to false in the same file changes that: any value outside the returned list then 404s instead of rendering on demand.
// app/blog/[slug]/page.tsx
export const dynamicParams = false;This option belongs to the caching model that predates Cache Components, so confirm which model a project uses before relying on it, since Cache Components changes how dynamic segments without a static value are validated at build time.
Common mistake
Returning an empty array by mistake, rather than omitting the function entirely, quietly disables build-time prerendering for that route instead of causing an error. An empty array is actually a valid way to defer every value to request time, so it fails silently rather than loudly, which makes it easy to miss during a code review.
For how these values reach the page itself, see page.js props, params, and search params. For the underlying dynamic segment convention this function fills in, see dynamic routes in Next.js.
Rune AI
Key Insights
- generateStaticParams returns an array of objects, one per route to prerender.
- Each object's keys must match the dynamic segment names in the file path.
- Values it does not return still render on request unless dynamicParams is false.
- A child segment's function receives the parent's resolved params as an argument.
- fetch calls inside it are deduplicated against the same requests in the page itself.
Frequently Asked Questions
What happens to a value not returned by generateStaticParams?
Can generateStaticParams run in a layout instead of a page?
Does generateStaticParams work with route handlers?
Conclusion
generateStaticParams returns the list of dynamic segment values Next.js should prerender at build time. Values outside that list still render normally on first request unless dynamicParams is turned off.
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.