For routes like /blog/[slug], you usually want to build all known posts at build time so each is a static HTML file served from a CDN.
1// app/blog/[slug]/page.tsx
2export async function generateStaticParams() {
3 const posts = await db.post.findMany({ select: { slug: true } });
4 return posts.map((p) => ({ slug: p.slug }));
5}
6
7export default async function Post({ params }: { params: Promise<{ slug: string }> }) {
8 const { slug } = await params;
9 const post = await getPost(slug);
10 return <Article post={post} />;
11}At build, Next.js calls generateStaticParams() once, then renders Post for every slug returned. Each becomes a .html file in .next/server/app/blog/<slug>.html.
By default (dynamicParams = true), if a user requests an unknown slug, Next.js renders it dynamically — and (if caching is enabled) caches the result for subsequent visitors.
To disallow unknown slugs (404 instead):
1export const dynamicParams = false;1// app/blog/[year]/[slug]/page.tsx
2export async function generateStaticParams() {
3 const posts = await db.post.findMany({ select: { year: true, slug: true } });
4 return posts.map((p) => ({ year: String(p.year), slug: p.slug }));
5}1// app/[lang]/[category]/[slug]/page.tsx
2export async function generateStaticParams() {
3 return [
4 { lang: "en", category: "tech", slug: "react-19-is-here" },
5 { lang: "en", category: "design", slug: "color-systems" },
6 { lang: "hi", category: "tech", slug: "react-19-aaya-hai" },
7 ];
8}The return is a flat list of {param: value} tuples — Next builds each combination once.
For sites with tens of thousands of dynamic pages:
1export async function generateStaticParams() {
2 // Pre-render the most popular 1,000 — the long tail renders on-demand
3 return db.post.findMany({
4 where: { status: "PUBLISHED" },
5 orderBy: { views: "desc" },
6 take: 1000,
7 select: { slug: true },
8 });
9}This is partial pre-rendering of dynamic routes: hot pages are static-fast; rare pages render JIT and then become cached.
Combine with a revalidate export to refresh post HTML on a cadence:
1export const revalidate = 3600; // hourlyOr use tag-based invalidation from your CMS webhook → revalidateTag(post:${slug}).