NextjsgenerateStaticParams

generateStaticParams

A dynamic route like 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

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>
  )
}
At build time, Next.js calls 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
For a route with multiple dynamic segments, return an object with a key for each segment. If a parent segment also has its own 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

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
By default, visiting a slug that wasn't returned by 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

TSX
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 }))
}
Note
Setting 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.
Tip
Think of 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.
  • generateStaticParams is 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 = false is set.

  • dynamicParams = false 404s 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.