Build Once, Serve Forever
Static Generation
Pages with no dynamic inputs are prerendered at build time and served from the CDN — the fastest mode Next.js has.
What you'll learn
- Build a static page with no cookies, headers, or dynamic searchParams
- Use `generateStaticParams` for dynamic segments
- Verify the static output in the build logs
A statically generated page is plain HTML produced by next build and served from cache
to every visitor. No server work happens at request time — that is why static pages are
the fastest mode Next ships.
A Static Page
// app/blog/page.tsx
export default async function Page() {
const data = await fetch('https://api.example.com/posts', {
cache: 'force-cache',
}).then((r) => r.json())
return (
<ul>
{data.map((p: { id: string; title: string }) => (
<li key={p.id}>{p.title}</li>
))}
</ul>
)
} The page reads nothing request-specific, and the fetch defaults to caching. Next will
prerender this once at build time.
Dynamic Segments — generateStaticParams
For routes like app/blog/[slug]/page.tsx, export a function that lists which params to
prerender:
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then((r) => r.json())
return posts.map((p: { slug: string }) => ({ slug: p.slug }))
}
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
return <h1>{slug}</h1>
} Every returned object becomes a prerendered HTML file at build time.
Reading the Build Output
When next build finishes, look for symbols next to each route:
○— static (prerendered HTML)ƒ— dynamic (rendered on demand)◐— ISR (static, regenerated periodically)
If a page you expected to be static shows ƒ, something inside it pulled in
request-specific data. Trace it back and remove the culprit.