Static, ISR, SSR, CSR — the spectrum
Web pages live somewhere on a line from "fully static, served from a CDN" to "fully dynamic, rendered per request in JavaScript on the client." Next.js supports the entire spectrum and lets you pick per route or per fetch.
| Strategy | When it's chosen | Use cases |
|---|---|---|
| SSG (build time) | No request-time data, fetches are cached | Docs, blog posts, marketing pages |
| ISR (revalidate) | Static + a revalidate hint | Product pages, news feed, pricing |
| SSR (per request) | Reads cookies(), headers(), searchParams | Auth'd pages, dashboards, search |
| CSR (browser) | Inside a Client Component using state | Live editors, collaborative UI |
How Next.js detects which mode you're in
You don't pick a mode by name — you pick it by the APIs you call. Use cookies() or headers() and the route becomes dynamic. Mark a fetch with cache: 'force-cache' and that data point is static. The detection is automatic at build time.
Why this matters before you write a line
Picking the wrong point on the spectrum is how Next.js apps end up either expensive (everything dynamic) or stale (everything static). Decide per route: what changes, how often, who pays the freshness cost.
Choose rendering behavior per route and data dependency from freshness, personalization, latency, and compute cost. Cache stable public reads, render request-specific state dynamically, and make the boundary explicit when the framework's inference would hide an important product promise. Static and dynamic are not application-wide modes. One route can combine cached data, request-bound values, client interaction, and streamed boundaries. Forcing everything to one end of the spectrum either wastes server work or serves stale and impersonal results. Render the same screen as static, revalidated, and request-specific output. Compare response headers and server logs on the first request, inside and outside the cache window, and with a personalization cookie so the actual execution lifetime is visible.