NextjsStatic Site Generation (SSG)

Static Site Generation (SSG)

Static Site Generation means the HTML for a page is generated once, at build time, and then reused for every subsequent request. There's no per-request rendering work at all — the server (or, more often, a CDN) simply hands back the same pre-built HTML file every time.

When SSG Is the Right Fit
  • Marketing pages, landing pages, pricing pages — content that rarely changes.

  • Blog posts and documentation — written once, updated occasionally through a new deploy.

  • Any page where every visitor should see essentially the same content.

The Default Behavior

In the App Router, static generation isn't something you opt into with a special function — it's the default outcome for any route that doesn't use a dynamic data source. If a Server Component page doesn't read cookies, doesn't read headers, doesn't use an uncached fetch, and doesn't read searchParams, Next.js renders it once at build time and serves that same HTML to everyone.

TSX
// app/about/page.tsx
export default function AboutPage() {
  return (
    <div>
      <h1>About Our Company</h1>
      <p>We build tools for developers.</p>
    </div>
  )
}

// No dynamic data sources at all -> statically generated at build time.
Static Generation With Data Fetching

SSG isn't limited to pages with no data fetching — a Server Component that fetches from an API using the default caching behavior of fetch is still eligible for static generation, since the result is treated as safe to compute once and reuse.

TSX
// app/blog/[slug]/page.tsx
async function getPost(slug: string) {
  // Default fetch caching -> this call's result can be baked into the static HTML.
  const res = await fetch(`https://api.example.com/posts/${slug}`)
  return res.json()
}

export default async function BlogPostPage({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug)
  return <article>{post.title}</article>
}
generateStaticParams: Pre-rendering Dynamic Routes

A dynamic route segment like [slug] doesn't know ahead of time which specific slugs exist, so by default Next.js can't pre-render every possible URL at build time. The generateStaticParams function solves this — export it alongside the page, and Next.js will call it at build time to discover the full list of params to pre-render.

TSX
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await getAllPosts()
  return posts.map((post) => ({ slug: post.slug }))
}

export default async function BlogPostPage({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug)
  return <article>{post.title}</article>
}

// Every slug returned above gets its own pre-rendered HTML page at build time.
Note
generateStaticParams gets its own dedicated lesson later in this series — for now, just know it exists as the App Router's answer to the Pages Router's getStaticPaths, and it's what makes dynamic routes fully static-generatable.
SSG vs SSR at a Glance

Aspect

SSG

SSR

When HTML is generated

Once, at build time

On every request

Freshness

Reflects data as of the last build/deploy

Always current

Performance

Extremely fast — served from cache/CDN

Slower — real rendering work per request

Best for

Unchanging or rarely-changing content

Personalized or fast-changing content

Tip
Default to writing pages as if they'll be statically generated — avoid reaching for cookies, headers, or uncached fetches unless the page genuinely needs them. You get the performance benefits of SSG for free, and only opt into SSR's cost where it's actually required.

SSG is the App Router's quiet default: the fastest, cheapest rendering strategy, automatically applied to any route that doesn't ask for something request-specific. When content needs to update more often than a full redeploy allows, Incremental Static Regeneration — building on everything covered here — is usually the next place to look.