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
// 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.
// 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.
// 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.
// 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