generateStaticParams at Scale

Prerender dynamic routes at build time, blend static and on-demand generation, and avoid building millions of pages you don't need.

generateStaticParams is how a dynamic segment like [slug] becomes many static pages at build time — it is the App Router's replacement for the old getStaticPaths. Return the list of params you want prerendered, and Next builds each into the Full Route Cache.

The scaling decision: dynamicParams

You rarely want to prerender every possible page — an e-commerce site with a million products would build for hours. The pattern is to prerender the popular subset and let the rest render on first request:

  • dynamicParams = true (the default): params not in the list are rendered on-demand the first time they are visited, then cached. Best of both worlds.
  • dynamicParams = false: params not in the list return a 404. Use for a closed, known set (a fixed docs table of contents).

Nested and dependent params

For multi-segment routes you return objects with every param key. You can also fetch inside generateStaticParams — read from your CMS to produce the list. Data fetched here is memoized and reused by the page render, so it is not a double fetch.

Example

Example · typescript
// app/blog/[slug]/page.tsx
// Prerender the 100 most recent posts at build time;
// older posts render on first visit, then get cached.
export const dynamicParams = true; // default — kept explicit

export async function generateStaticParams() {
  const posts = await getRecentPosts(100);
  return posts.map((p) => ({ slug: p.slug }));
}

export default async function Post({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const post = await getPost(slug); // memoized with the call above
  if (!post) notFound();
  return <article>{post.title}</article>;
}

// Multi-segment example:
// return categories.flatMap((c) =>
//   c.products.map((p) => ({ category: c.slug, product: p.slug }))
// );

When to use it

  • A blog with 500 posts uses generateStaticParams to pre-render all of them at build time for instant delivery.
  • A large catalogue uses generateStaticParams with a page limit and dynamicParams = true to build top products and generate the rest on-demand.
  • A monorepo CI pipeline uses generateStaticParams on a filtered subset of params to keep build times under 10 minutes.

More examples

generateStaticParams for blog

generateStaticParams runs at build time and tells Next.js every slug to pre-render as a static HTML file.

Example · ts
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await db.post.findMany({ select: { slug: true } });
  return posts.map(p => ({ slug: p.slug }));
}

export default async function BlogPost({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  const post = await db.post.findUnique({ where: { slug } });
  return <article>{post?.body}</article>;
}

Partial pre-rendering with on-demand fallback

dynamicParams = true allows unknown ids to render on demand while the top-100 products are pre-built at deploy time.

Example · ts
// app/products/[id]/page.tsx
export const dynamicParams = true; // allow on-demand generation for new ids

export async function generateStaticParams() {
  const top = await db.product.findMany({ orderBy: { views: 'desc' }, take: 100 });
  return top.map(p => ({ id: String(p.id) }));
}

Disable on-demand for strict SSG

dynamicParams = false makes Next.js return a 404 for any path not returned by generateStaticParams, enforcing a closed set.

Example · ts
// app/archive/[year]/page.tsx
export const dynamicParams = false; // 404 for any year not in params

export async function generateStaticParams() {
  return [{ year: '2023' }, { year: '2024' }, { year: '2025' }];
}

Discussion

  • Be the first to comment on this lesson.
generateStaticParams at Scale — Next.js | SoundsCode