New54 new lessons added since Sep 10!
Explore What's New →
Software Development Atlas
Engineering JudgmentDecision Guides

CSR vs SSR vs SSG

Choose a web rendering model by freshness, personalization, interactivity, cacheability, server work, and operational constraints instead of framework fashion.

EvolvingVerified Sep 9, 2026Review target: 180 days

Personal learning atlas by Tran Trong Thuc · About this Atlas · Atlas last updated Sep 22, 2026

TL;DR

A fast-growing e-commerce brand migrated its storefront to a pure Client-Side Rendered (CSR) Single Page Application to give developers a unified React workflow. The migration triggered a business disaster: organic search rankings plummeted 60% within weeks because search engine crawlers deferred or dropped the empty initial HTML shell. Worse, mobile shoppers on average 3G connections stared at a blank white screen for 4 full seconds before the massive JavaScript bundle finished downloading and parsing—sending bounce rates through the roof. Choosing a web rendering model is not about framework fashion; it is an engineering calculation balancing Time to First Byte (TTFB), First Contentful Paint (FCP), the interactive hydration gap, and server infrastructure overhead.

💡 Rule of thumb: Choose rendering strategies by route and data lifecycle, not by application-wide dogmatism. Prefer static generation (SSG/ISR) 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 (SSR) when useful initial HTML must include request-time state that cannot safely or acceptably be represented by a shared static response; and center client-side rendering (CSR) for private, authenticated workspaces where the initial document shell is generic and value unfolds in a rich client session.

  • SSG delivers edge cacheability and instant first paint: Pre-generating HTML at build time or via background revalidation achieves sub-50ms TTFB worldwide, shielding database clusters from request-time traffic surges.
  • SSR embeds request-time state on the initial network hop: Server-rendering delivers fully populated semantic HTML for immediate crawler indexing and human perception, but places rendering CPU and database queries directly onto the critical request path.
  • CSR offloads page-generation work to the browser: Serving a static shell reduces server compute costs, but forces users to pay a heavy upfront download, parse, and execution tax for client JavaScript bundles before seeing useful content.
  • Mind the interactive hydration gap: SSR and SSG deliver early visual content (FCP), but complex applications can trap users in an "uncanny valley" where buttons look clickable but remain completely unresponsive until client hydration finishes.
  • Fatal pitfall: Defaulting to SSR for high-traffic public catalog routes without caching. Hitting un-memoized database queries on every incoming page render turns your web servers into an instant scalability bottleneck under traffic spikes. Always decouple public layouts into cached static shells (SSG/ISR) and fetch personalized, volatile fragments asynchronously via client APIs.

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.

CSR vs SSR vs SSG delivery timeline
CSR
HTML shell
Download + execute JS
Render content
Interactive
SSR
Useful HTML
Download JS
Hydrate
Interactive
SSG
Prebuilt HTML from CDN
Optional JS
Hydrate interactive regions
The strategies move HTML generation and JavaScript work to different points in the request and build lifecycle.

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.

The hydration gap
  1. Server HTML arrives
    Content is visible
  2. Hydration gap
    Looks interactive, handlers not ready yet
  3. Client runtime hydrates
    State + handlers attach
  4. Interactive UI
    User actions are handled
SSR can make a page look ready before event handlers and client state are attached.

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.

Static revalidation lifecycle
  1. Fresh cached page
    Serve immediately
  2. Entry becomes stale
    Revalidation becomes eligible
  3. Regenerate in background
    Keep serving according to cache policy
  4. Publish refreshed entry
    Future requests see new content
A stale response can be served while regeneration happens in the background, depending on the framework and cache contract.

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.

CSR vs SSR vs SSG decision matrix
CriterionCSRSSRSSG
Request-specific initial HTMLInitial shell is usually generic; request-specific data arrives after client code runsCan include request-time state in the initial HTMLNeeds a dynamic layer when the initial response must differ per request
Very fresh dataClient can fetch current data after startupServer can fetch current data on the request path, subject to cache policyRequires sufficiently frequent publish/revalidation or a dynamic/client data path
Useful HTML before app JavaScript runsLimited to what the shell already containsAvailable when the server render succeedsAvailable from the generated artifact
Long-lived application interactivityClient runtime naturally owns the ongoing UI sessionStill requires client activation for interactive regionsStill requires client JavaScript for interactive regions
Shared response reuseShell/assets can be shared; personalized API data is separateDepends on which request attributes vary the rendered response and cache keyHigh when the same generated representation can be reused
Page-generation work on each requestNo full page render is required on the server; APIs may still do request-time workPresent unless a cached rendered response is reusedAbsent for normal delivery after generation; revalidation/publishing does the generation work
Freshness mechanismClient data requests and client cache policyRequest-time data access and/or cached SSRBuild, publish, or revalidation plus optional dynamic regions
Primary operational burdenClient bundle/data-loading/state failures plus backend APIsLatency-sensitive render capacity, data access, caching, and client activationBuild/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

Hybrid rendering inside one product

Shared shell

Header / footer
SSG + CDN

Product content

Request-aware detail
SSR

Personal regions

Cart / recommendations
CSR after load
Rendering labels describe where specific work happens; a product can combine them by route or region.

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

An e-commerce team switched their product catalog from SSG to pure SSR to display live inventory badges. Under normal traffic, TTFB looked acceptable at 180ms. But during a marketing campaign, the SSR server made 4 unmemoized database queries per page render. Under 5,000 req/sec, the rendering servers overloaded the database, TTFB spiked from 180ms to 9.2 seconds, and the site effectively went down—even though 95% of the page content was identical for every visitor:

  • Impact: The store suffered a 45-minute outage during peak shopping hours, causing severe revenue loss and damaging search rankings.
  • Root cause: Abusing whole-page SSR for a small, volatile data field (stock counts), placing un-cached database queries directly onto the synchronous HTML render path.
  • Correct pattern: Adopt a hybrid rendering architecture:
    1. Serve the shared product catalog skeleton via SSG/ISR from edge cache with TTFB < 50ms.
    2. Fetch dynamic inventory status asynchronously on the client (CSR) using a short-lived cache (e.g. 10 seconds) or a lightweight WebSocket connection.

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:

  1. Can the same initial representation be reused across many requests? If yes, test whether static generation plus caching can satisfy the freshness requirement.
  2. Must useful initial HTML include request-time state? If yes, consider SSR for that surface.
  3. 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.
  4. 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.
  5. 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.

Check your mental model

Scenario: A SaaS startup is launching an analytics dashboard. Users log in to view charts generated from their private telemetry data. The marketing VP insists: "We must use Server-Side Rendering (SSR) for every dashboard route so Google can index our customer reports and improve our SEO ranking."

How would you evaluate this reasoning in an architecture review?

Show the reasoning

Why this reasoning conflates distinct requirements:

  1. Search engines do not index authenticated private pages: Search crawlers (such as Googlebot) do not log in as specific authenticated users. A private analytics dashboard behind an auth wall will never appear in public search results regardless of whether it is rendered via SSR or CSR.
  2. Unnecessary server load: Using SSR for a private, data-dense dashboard forces server compute onto every page view, requiring database queries on the critical request path.
  3. Better architecture: Use a fast, static generic shell (CSR) cached on a CDN, with an authenticated API or GraphQL endpoint fetching telemetry data asynchronously after page load. Reserve SSR and SSG for public marketing and landing pages where SEO indexing and unauthenticated first-load performance actually matter.

Decision review checklist

Use this checklist during architecture design reviews before committing to a rendering strategy:

  • First visual need: What content must be visible in HTML before application JavaScript executes?
  • Data boundary: Which initial data is public/shared, request-specific, or continuously changing?
  • Allowed staleness: What is the product-acceptable staleness window, and can publishing/revalidation satisfy it?
  • Cache keys & variation: Which responses can be cached safely, and what request headers/cookies vary their cache key?
  • Client execution budget: How much client JavaScript bundle size and CPU execution time are required before interactive elements respond?
  • Critical request path: What database or upstream API calls are placed directly onto the user-facing latency path?
  • Failure isolation: Can the team monitor, isolate, and recover from rendering, upstream data-access, cache, and client-activation failures independently?

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

On this page