Static Generation

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.

4 min read Level 2/5 #nextjs#ssg#static
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.

Incremental Static Regeneration →