generateStaticParams
app/blog/[slug]/page.tsx can serve infinitely many URLs, but Next.js has no way to know which slugs actually exist — unless you tell it. generateStaticParams is an async function you export from a dynamic route that returns the list of param values to pre-render as static HTML at build time.Worked example: blog slugs
app/blog/[slug]/page.tsx
import { getAllPosts, getPost } from '@/libs/posts'
export async function generateStaticParams() {
const posts = await getAllPosts()
return posts.map((post) => ({
slug: post.slug,
}))
}
export default async function BlogPostPage({
params,
}: {
params: { slug: string }
}) {
const post = await getPost(params.slug)
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
)
}generateStaticParams once, gets back (say) 200 { slug } objects, and pre-renders 200 static HTML pages — one per post — all before the app ever serves a single request. Visitors get instantly-served static HTML instead of waiting for a per-request render.Nested dynamic segments
generateStaticParams, Next.js can combine the two so child routes only generate combinations that make sense for their parent.app/shop/[category]/[product]/page.tsx
export async function generateStaticParams() {
const products = await getAllProducts()
return products.map((product) => ({
category: product.categorySlug,
product: product.slug,
}))
}What happens to params you didn't list
generateStaticParams still works — Next.js renders it on demand at request time and caches the result, a pattern known as Incremental Static Regeneration (ISR) for new params. This is a practical default: you pre-render the posts that exist today at build time, and any post published afterward still renders correctly the first time someone requests it.app/blog/[slug]/page.tsx — controlling unknown params
export const dynamicParams = false // 404 for any slug not returned above
export async function generateStaticParams() {
const posts = await getAllPosts()
return posts.map((post) => ({ slug: post.slug }))
}dynamicParams = false makes any param not returned by generateStaticParams result in a 404, instead of an on-demand render. Use this when the full set of valid params is genuinely closed and known at build time — for example, a fixed set of legal-document versions — so a typo in a URL fails loudly instead of quietly rendering something unexpected.generateStaticParams as choosing which pages get the "built once, served many times" treatment. Combine it with revalidation (via fetch's revalidate option or route segment config) so those statically built pages still pick up content updates on a schedule, instead of staying frozen at their build-time content forever.generateStaticParamsis an async function exported from a dynamic route that returns the list of param objects to pre-render at build time.Each returned object's keys must match the route's dynamic segment names (e.g.
{ slug }for[slug]).Params not returned still render on demand at request time by default (ISR-style) unless
dynamicParams = falseis set.dynamicParams = false404s any param outside the pre-rendered set — use it when the full set of valid values is fixed and known.Pair static params with a revalidation strategy so pre-rendered pages stay fresh instead of freezing at build-time content.