Guide

SSR vs CSR vs SSG vs ISR explained: choosing a web rendering strategy

Every page you ship must answer one question: where does the HTML come from? Client-side rendering (CSR) builds the page in the browser after JavaScript loads. Server-side rendering (SSR) builds it on each request. Static site generation (SSG) builds it once at deploy time. Incremental static regeneration (ISR) is SSG with a refresh schedule. The choice shapes first paint speed, search indexing, hosting cost, and how painful deploys feel. This guide explains how each pattern works, what hydration means, how HTTP caching interacts with rendering, and a practical framework for picking the right default for your product.

The rendering pipeline in one picture

A web page is HTML (structure), CSS (presentation), and JavaScript (behavior). The rendering strategy decides when those pieces are assembled relative to the user's request:

  • Build time — CI or a static generator runs your app, fetches data, and writes finished HTML files to disk (SSG, ISR background rebuilds).
  • Request time (server) — a Node, Go, or edge function runs your framework, fetches fresh data, and streams HTML to the browser (SSR).
  • Request time (client) — the server sends a thin HTML shell plus a large JS bundle; the browser downloads, parses, executes, fetches APIs, and paints the UI (CSR).

Modern frameworks blur the lines: Next.js, Nuxt, SvelteKit, and Astro let you mix SSG for marketing pages, SSR for personalized dashboards, and CSR islands for highly interactive widgets on the same site. The vocabulary still matters because each mode has different failure modes and caching rules.

Client-side rendering (CSR)

In pure CSR — classic Create React App, Vite + React SPA, many Vue single-page apps — the server returns nearly empty HTML:

<div id="root"></div>
<script src="/assets/app.js"></script>

The browser downloads the bundle (often hundreds of kilobytes gzip), executes it, calls your REST or GraphQL APIs, and only then renders content. Users on slow networks see a blank or loading spinner until the JS waterfall completes. Search engines have improved at executing JavaScript, but crawlers still prefer HTML that arrives in the first response — CSR pages can index late or incompletely if your data fetch is slow or fails client-side only.

When CSR shines

  • Authenticated dashboards where SEO does not matter and every view is behind login.
  • Heavy interactivity — trading terminals, canvas games, design tools — where server round-trips per interaction would feel sluggish.
  • Simple hosting — any static file server or object storage bucket; no Node runtime required.

CSR pitfalls

  • Slow Largest Contentful Paint (LCP) when the LCP element depends on client fetches.
  • Large JS bundles hurt mobile users and low-end devices disproportionately.
  • Social preview bots that do not execute JS may show empty Open Graph cards unless you add a prerender or edge meta-tag service.

Server-side rendering (SSR)

SSR runs your application on the server for each incoming request (or caches the result briefly). The first byte of HTML already contains meaningful content — headlines, article body, product listings — so users and crawlers see real text before JavaScript downloads. After HTML arrives, the browser still downloads JS for hydration: attaching event listeners and making the static HTML interactive (clicks, form validation, wallet connect modals).

Hydration is not free. The server did work to render React/Vue components; the client repeats much of that work to reconcile the virtual DOM with existing DOM nodes. Mismatches — server rendered "3 items" but client fetches and shows "5 items" — cause hydration warnings and flicker. Frameworks mitigate this with streaming SSR (send HTML in chunks as data resolves) and selective hydration (only hydrate interactive islands).

When SSR shines

  • Personalized pages — "Welcome back, Alice" or locale-specific pricing that cannot be precomputed for every user at build time.
  • Fresh data on every visit — stock tickers, live sports scores, inventory counts that go stale within minutes.
  • SEO-sensitive pages with dynamic data — user-generated profiles where you still want crawlable HTML without maintaining a separate prerender pipeline.

SSR pitfalls

  • Time to First Byte (TTFB) includes server compute + database queries; slow SSR feels worse than fast CSR with a good skeleton UI.
  • Requires a always-on runtime (Node, Deno, edge workers) — higher ops surface than flat files.
  • Harder to cache at the CDN edge unless you segment by cookie, geography, or cache tags carefully.

Static site generation (SSG)

SSG pre-renders pages at build time. Your CI pipeline runs next build, astro build, or Hugo/Jekyll, producing HTML, CSS, and JS artifacts uploaded to a CDN. When a user requests /guides/example/, the edge serves a file from disk — no database hit, no Node process. Latency is minimal and hosting is cheap at scale.

Solana Garden's guides and news articles are SSG: each page is a self-contained HTML file with embedded metadata and Article JSON-LD so crawlers get complete documents from the first response. That pattern is ideal for content that changes when an editor publishes, not on every page view.

When SSG shines

  • Marketing sites, blogs, documentation with a bounded set of URLs.
  • Maximum performance and reliability — no runtime dependency at request time.
  • Strong cacheability — set long Cache-Control on fingerprinted assets and shorter TTL on HTML, or immutable caching for fully static deploys.

SSG pitfalls

  • Build time grows with pages — 100,000 product SKUs mean 100,000 prerenders unless you paginate or fall back to SSR for the long tail.
  • Stale until redeploy — fixing a typo requires a new build, not a database update.
  • Dynamic per-user content is awkward without client-side fetches after load (hybrid pattern).

Incremental static regeneration (ISR)

ISR (popularized by Next.js) combines SSG's speed with periodic freshness. You pre-render a page at build time and set a revalidate interval — say 60 seconds. The first visitor after expiry still gets the stale cached HTML instantly (no slow SSR), while a background job regenerates the page for the next visitor. On-demand revalidation lets your CMS webhook purge or rebuild specific paths when content publishes without waiting for the timer.

Think of ISR as "SSG with a refresh policy." It suits e-commerce category pages, news indexes, and documentation that updates hourly but does not need millisecond-fresh prices. It is a poor fit for authenticated account settings or checkout flows where every user sees different data.

ISR trade-offs

  • Visitors may briefly see stale content — acceptable for blogs, risky for inventory "only 1 left" badges.
  • Requires platform support (Vercel, Netlify builders, custom cache invalidation) — not just any static host.
  • Debugging "why is this page old?" involves cache layers, revalidate timers, and CDN purge logs.

Comparing the four patterns

Pattern HTML ready when SEO / crawlers Data freshness Hosting
CSR After JS + API calls Weaker unless prerendered Live (client fetches) Static files only
SSR First server response Strong Per request Server / edge runtime
SSG At deploy (CDN) Strong Stale until rebuild Static CDN
ISR CDN (may be stale) Strong Background refresh Platform with revalidation

Hybrid architectures are normal: SSG for your homepage and guides, SSR for search results, CSR islands for wallet-connected dashboards. The mistake is picking one global mode because the framework default said so.

Rendering and Web3 / dApp frontends

Crypto apps add constraints: wallet extensions inject window.solana only in the browser, RPC calls are rate-limited, and transaction signing must never run on a shared server holding user keys. The safe split:

  • SSG or SSR for marketing, docs, and SEO content — explainers, guides, landing pages.
  • CSR (or SSR shell + client hydration) for wallet flows — connect, sign, send must execute client-side; server verifies payments via on-chain confirmation, not by rendering private keys.
  • Real-time updates — balances and mempool status often use WebSockets or SSE after the initial HTML shell loads, regardless of SSR vs CSR.

Do not SSR secret API keys into HTML for RPC providers — environment variables leak in view-source. Proxy RPC through your backend with rate limits and auth, or call public endpoints from the client with the understanding that users can bypass your UI.

Decision framework

  1. Does this URL need to rank in Google? If yes, avoid pure CSR for primary content. Prefer SSG for stable pages, SSR for dynamic but crawlable routes.
  2. How often does data change? Rarely → SSG. Hourly → ISR. Every request → SSR. Per-user after login → CSR or SSR with private caching disabled.
  3. What is your p95 TTFB budget? If DB queries exceed 200ms, SSR may need edge caching or partial prerendering — or move the slow query client-side after a static shell.
  4. How many unique URLs? Thousands of static paths → SSG. Millions of SKUs → SSR or ISR with on-demand generation for the long tail.
  5. Ops tolerance? Static CDN only → SSG. Already running Node APIs → SSR is incremental.

Measure after you choose: track LCP, INP, and TTFB in field data, not just Lighthouse in CI. A "slower" pattern with aggressive caching often beats a "faster" pattern with cold SSR on every hit.

Common mistakes

  • SPA everything — shipping CSR for public blog posts and wondering why organic traffic flatlines.
  • SSR without caching — rendering the same popular page 10,000 times per minute from Postgres.
  • Hydration mismatch — rendering random IDs or Date.now() on server and client differently.
  • Blocking interactivity on full hydration — users cannot click until the entire tree hydrates; use islands or progressive enhancement.
  • Ignoring cache headers — SSR HTML cached at CDN with wrong Cache-Control leaks one user's data to another.
  • Build-time API calls in CI — SSG builds fail when staging APIs are down; add fallbacks or mock data for non-production builds.

Key takeaways

  • CSR defers rendering to the browser — great for apps, weak for SEO-first content.
  • SSR sends complete HTML per request — strong SEO and personalization at server cost.
  • SSG precomputes HTML at deploy — fastest and cheapest for bounded, editorial content.
  • ISR refreshes static pages on a schedule — a middle ground for semi-fresh catalog and news indexes.
  • Hybrid per-route rendering is the modern default; match the pattern to each URL's SEO and freshness needs.
  • Wallet and signing logic stay client-side; use SSG/SSR for everything crawlers and first-time visitors should see.

Related reading