CSR vs SSR vs SSG
Choose a web rendering model by freshness, personalization, interactivity, cacheability, server work, and operational constraints instead of framework fashion.
Personal learning atlas by Tran Trong Thuc · About this Atlas · Atlas last updated Sep 9, 2026
TL;DR
Do not choose CSR, SSR, or SSG by asking which one is "modern." Ask when useful HTML must exist, whether that HTML depends on request-time state, how much staleness the product allows, how much client JavaScript is required, and which work you are prepared to operate at build time, request time, and in the browser.
A practical starting rule is:
- prefer static generation when the same generated representation can be reused across many requests and its freshness can be maintained by publishing or revalidation;
- use request-time server rendering when useful initial HTML must include request-time state that cannot safely or acceptably be represented by a shared static response;
- center client-side rendering when the initial document can be a generic shell and most product value appears after client code and data have loaded.
These are not mutually exclusive product identities. Modern applications commonly mix them by route, component, and data dependency.
Decision frame
Before naming a framework, write down the constraints:
- What must be present in the initial HTML before application JavaScript runs?
- Is that initial representation public and shared, or does it depend on the current request/user?
- How stale may the representation become before the product is wrong or misleading?
- Is the page mainly reading/navigation, or a long-lived interactive workspace?
- Which responses can be reused safely through HTTP/CDN caching, and what changes the cache key?
- What latency budget exists between request arrival and useful HTML?
- How much client JavaScript is required before the important interactions work?
- Which failures can the team operate reliably: build/revalidation failures, request-time rendering failures, client data-loading failures, or some combination?
Those answers narrow the choice more reliably than a framework feature checklist.
The options
Client-side rendering (CSR)
With CSR, the server usually sends a generic HTML shell plus JavaScript. The browser runs application code, obtains data, and creates or updates the useful UI.
Choose CSR as the center of gravity when the initial HTML does not need request-specific application state and the product already depends on a long-lived client session, rich local state, and repeated API interaction. The cost is explicit: initial usefulness depends more heavily on JavaScript download/execution and client-side data acquisition.
Server-side rendering (SSR)
With SSR, the server produces useful HTML for a request and may incorporate request-time data. Interactive applications commonly execute client JavaScript afterward to attach behavior to that server-rendered HTML.
Choose SSR when useful initial HTML must include request-time state, or when moving data access into the server request path avoids an unacceptable browser-to-API waterfall. Rendering then becomes part of the request path, so capacity, cache policy, latency, timeout behavior, and rendering failures become production concerns.
Static site generation / prerendering (SSG)
With SSG, HTML is produced before an individual request, usually during a build, publish, or revalidation process. The generated artifact can then be served without performing a full page render for every request.
Choose SSG when many requests can safely receive the same generated representation and the allowed staleness window can be met through publishing or revalidation. Request-specific or continuously changing regions need another mechanism.
| Criterion | CSR | SSR | SSG |
|---|---|---|---|
| Request-specific initial HTML | Initial shell is usually generic; request-specific data arrives after client code runs | Can include request-time state in the initial HTML | Needs a dynamic layer when the initial response must differ per request |
| Very fresh data | Client can fetch current data after startup | Server can fetch current data on the request path, subject to cache policy | Requires sufficiently frequent publish/revalidation or a dynamic/client data path |
| Useful HTML before app JavaScript runs | Limited to what the shell already contains | Available when the server render succeeds | Available from the generated artifact |
| Long-lived application interactivity | Client runtime naturally owns the ongoing UI session | Still requires client activation for interactive regions | Still requires client JavaScript for interactive regions |
| Shared response reuse | Shell/assets can be shared; personalized API data is separate | Depends on which request attributes vary the rendered response and cache key | High when the same generated representation can be reused |
| Page-generation work on each request | No full page render is required on the server; APIs may still do request-time work | Present unless a cached rendered response is reused | Absent for normal delivery after generation; revalidation/publishing does the generation work |
| Freshness mechanism | Client data requests and client cache policy | Request-time data access and/or cached SSR | Build, publish, or revalidation plus optional dynamic regions |
| Primary operational burden | Client bundle/data-loading/state failures plus backend APIs | Latency-sensitive render capacity, data access, caching, and client activation | Build/revalidation correctness plus any dynamic escape hatches |
When each option fits
Prefer CSR when
- the product is an authenticated dashboard, editor, or workspace where most value appears after interaction begins;
- the initial HTML does not need user-specific application state;
- rich client state and repeated API interactions are already central to the product;
- the team can keep JavaScript size, loading states, errors, and client performance within product budgets.
A CSR choice does not mean "put all logic in the browser." Authentication, authorization, durable state, and trust boundaries remain backend concerns.
Prefer SSR when
- useful initial HTML must include request-specific or rapidly changing state;
- delivering that HTML before the client runtime becomes ready matters to the user experience;
- request-time server data access avoids an otherwise unacceptable client-side data waterfall;
- the team can operate rendering as part of the latency-sensitive request path.
SSR does not guarantee a fast page. Slow server data fetching, serial work, poor caching, or a large client activation payload can still dominate latency and interactivity.
Prefer SSG when
- many requests can receive the same initial representation;
- publishing or revalidation can satisfy the freshness contract;
- broad CDN/cache reuse is safe for that representation;
- avoiding page-generation work on the normal request path simplifies operations or improves latency consistency.
Documentation, marketing pages, public reference material, and some catalog/detail pages often meet these conditions. The decision still depends on their actual freshness and personalization requirements.
Hybrid rendering is normal
The labels describe where particular rendering work happens, not mutually exclusive application identities.
A product can statically generate public documentation, server-render a request-specific account page, and run a client-rendered editor after navigation. A single route can also start from shared or server-rendered HTML and then use client-side data fetching for regions that update continuously.
The useful design unit is therefore the surface and its data, not an architecture acronym for the whole product.
Failure modes and hidden costs
Treating SEO as the only reason for server/static HTML
Search visibility can matter, but useful HTML before a large client runtime is ready can also matter for user experience. Conversely, a public page does not automatically require SSR; shared static HTML may satisfy the same requirement with less request-time work.
Ignoring hydration and client activation cost
Server-rendered HTML can still require substantial JavaScript before interactions work. React's hydrateRoot() requires the initial client output to match the server-rendered markup; mismatches are bugs, and React does not guarantee every mismatch will be patched automatically.
Assuming static means stale forever
Static generation is a production strategy, not a promise that content never changes. Publishing, revalidation, and targeted dynamic/client data paths can provide controlled freshness.
Assuming CSR removes server complexity
The browser may render the UI, but APIs still need authentication, authorization, caching, rate limits, observability, and failure semantics. CSR moves rendering work; it does not remove backend engineering.
Practical heuristic
Use these questions in order:
- Can the same initial representation be reused across many requests? If yes, test whether static generation plus caching can satisfy the freshness requirement.
- Must useful initial HTML include request-time state? If yes, consider SSR for that surface.
- Can the initial document be generic while most value appears during a long client session? If yes, CSR may be the simplest center of gravity.
- Can the route be split by data lifecycle? Keep shared/stable regions static or cached and use request-time/client work only where their data requirements demand it.
- What fails when this rendering path fails? Choose a model whose generation, caching, data access, and client-activation failures the team can detect and recover from.
Questions to ask before choosing
- What must be visible before application JavaScript executes?
- Which initial data is public/shared, request-specific, or continuously changing?
- What is the allowed staleness window?
- Which responses can be cached safely, and what changes their cache key?
- How much client JavaScript is required for the important interactions?
- What work is on the critical request path?
- Can the team diagnose rendering, data-access, cache, and client-activation failures separately?
Related concepts
This decision connects directly to Hydration, Frontend Data Fetching, HTTP Caching, CDN Behavior, and the browser rendering/runtime model. Use the Modern Web Systems path to place these trade-offs in the larger request lifecycle.
Sources
Engineering Judgment
Practice choosing among architectural options and tracing realistic systems across boundaries, failures, security, observability, and cost.
Monolith vs Modular Monolith vs Microservices
Choose deployment and domain boundaries by team ownership, transaction needs, failure isolation, scaling pressure, and operational capacity rather than architecture prestige.