Static rendering pre-renders a route's HTML at build time, so every visitor gets the same, instantly-served page with no per-request server work. This lesson explains how Next.js decides when a route qualifies for static rendering.
How Next.js Decides to Render Statically
In the App Router, static rendering is the default behavior for any route that doesn't use dynamic, request-specific data. If a route's Server Components only call fetch() with cacheable options (or use generateStaticParams for dynamic segments), Next.js renders the route's HTML once at build time and serves that same HTML to every visitor.
The moment a route reads something inherently request-specific — like cookies, headers, or search params in certain contexts, or uses cache: "no-store" — Next.js automatically switches that route to dynamic rendering instead, since the output can no longer be the same for every visitor.
// app/about/page.tsx — a good candidate for static rendering
export default function AboutPage() {
return <h1>About Our Company</h1>;
}
// This entire page can be rendered once, at build time,
// and served identically to every visitor.
No dynamic data, no per-request logic — Next.js renders this once and reuses the HTML for every request.
Cached fetch() calls are compatible with static rendering.
Reading cookies or headers makes a route dynamic, since that data varies per request.
cache: "no-store" explicitly opts a specific fetch (and often the route) out of static rendering.
You can force a route to be static or dynamic explicitly with a route segment config export.
Static Rendering Cheat Sheet
What keeps a route static vs. what forces it dynamic.
Behavior
Static Rendering
Rendered
Once, at build time
Served to every visitor
Same, pre-rendered HTML
Compatible with cookies()/headers()
No — forces dynamic rendering
Compatible with cached fetch()
Yes
Explicit opt-in
export const dynamic = "force-static";
Static Rendering for Dynamic Routes
Dynamic routes (like [slug]) can still be statically rendered — for every specific slug returned by generateStaticParams, Next.js generates a separate static HTML file at build time, even though the route itself uses a dynamic segment.
Statically rendered pages can be served directly from a CDN edge location, without invoking a server function at all, resulting in extremely fast responses and lower infrastructure cost at scale — which is why it's the preferred strategy whenever content doesn't need to be personalized per request.
Common Mistakes
Reading cookies or headers in a route that was intended to stay fully static, unintentionally making it dynamic.
Assuming a dynamic route ([slug]) can never be statically rendered.
Forgetting to export generateStaticParams, leaving dynamic routes rendered on-demand instead of pre-built.
Not distinguishing static rendering (this lesson) from Incremental Static Regeneration (which adds periodic revalidation).
Key Takeaways
Static rendering pre-renders a route's HTML once, at build time, for every visitor.
It's the default for routes without request-specific data or uncached fetches.
Reading cookies()/headers(), or using cache: "no-store", switches a route to dynamic rendering.
Dynamic routes can still be statically rendered per parameter value via generateStaticParams.
Static rendering offers the best performance and lowest cost for content that's the same for every visitor.
Pro Tip
Run next build and check its output summary — Next.js explicitly labels each route as Static, Dynamic, or ISR in the build log, which is the fastest way to confirm a route is being rendered the way you expect.
You now understand static rendering. Next, learn about server-side rendering — the strategy for content that must be fresh on every request.