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