frontend / rendering modes / 01_rendering_modes_matrix.md

CSR vs SSR vs SSG vs ISR vs Streaming SSR — The Matrix

7 min read source

CSR vs SSR vs SSG vs ISR vs Streaming SSR — The Matrix

TL;DR

Five (and a half) rendering modes for delivering HTML. CSR — JS renders in the browser, slow first paint, easy infra. SSR — server renders per request, fresh data, server cost. SSG — pre-rendered at build, fastest serve, stale on data change. ISR — SSG with periodic revalidation, hybrid freshness. Streaming SSR — HTML flushed in chunks as data resolves, fastest perceived load. Pick per-route based on: data freshness, SEO need, personalization, server cost budget.

Interview Q&A

Q: Quick comparison table.

A:

Mode When HTML is built TTFB Data freshness SEO Server cost Where state lives
CSR in browser (JS) fast (empty HTML) always fresh (fetch on mount) poor (needs SSR for crawlers) minimal (static host) client only
SSR per request, on server slow (waits for data) always fresh great high (compute per request) server-rendered + client hydrates
SSG at build time fastest (CDN-cached HTML) stale (until next build) great minimal (CDN serves) baked into HTML
ISR at build + on demand fast (cached) configurable (e.g., revalidate 60s) great low (cached) mostly baked, periodically refreshed
Streaming SSR per request, streamed fast (shell first) fresh great high server-rendered + client hydrates

Q: CSR — when does it still fit?

A: Pure client-side rendering (CRA, Vite SPA, no SSR) sends an empty <div id="root"></div> shell + JS bundle. JS hydrates with empty state, fetches data, then renders.

Pros:

  • Cheap infrastructure (static host: S3 + CDN, Vercel static, GitHub Pages).
  • Same dev model as building any React/Vue app.
  • Authoring is simple.

Cons:

  • Bad LCP — user sees blank screen until JS loads + renders.
  • Bad SEO — crawlers see empty HTML (some run JS, but unreliable).
  • No server data fetch — every page hits the API client-side, no caching at the edge.

Use when: internal tools behind login (SEO doesn’t matter), admin panels, SPAs where the audience tolerates the load wait (e.g., a Trello/Figma-like app).

Don’t use for: marketing sites, blogs, e-commerce, anything that needs SEO or fast first paint.

Q: SSR — what’s the trade?

A: Server renders HTML per request, sends complete page. Client hydrates.

Pros:

  • Fast LCP — HTML arrives with content.
  • Great SEO — crawlers see real content.
  • Fresh data — server fetches on each request.

Cons:

  • Slow TTFB — server waits for data before flushing.
  • Server cost — compute per request, not cacheable at the CDN.
  • Cold start matters in serverless.

Use when: per-request personalization, search results, dashboards, anything needing fresh data + SEO.

Q: SSG — how is it different?

A: Pages are rendered at build time, output as static HTML files, deployed to a CDN. Same URL → same HTML for everyone, until the next build.

Pros:

  • Fastest serve — CDN cache hit, nothing to compute.
  • Minimal server cost — pure static hosting.
  • Predictable perf — no cold starts, no DB latency.

Cons:

  • Stale on data change — until next build.
  • Build time grows with page count — a 100K-product e-commerce site can’t SSG every page.
  • No personalization — same HTML for all users.

Use when: marketing pages, docs, blogs with weekly cadence, low-frequency content.

Q: ISR — the hybrid.

A: Incremental Static Regeneration (Next.js-coined term, similar concepts elsewhere): SSG with a revalidation window.

// Next.js
export const revalidate = 60;   // re-generate at most every 60 seconds

export default async function Page() {
  const post = await fetchPost();
  return <Article post={post} />;
}

Behavior:

  • First request: render + cache.
  • Within 60s: serve cached.
  • After 60s: serve cached + trigger background re-render. Next request gets the fresh one.

This is stale-while-revalidate at the page level. Combines SSG’s speed with bounded staleness.

Use when: blog/news/product pages that update occasionally but don’t need instant freshness.

Q: Streaming SSR — what’s new?

A: SSR that flushes HTML in chunks as data resolves, instead of waiting for everything.

Without streaming: server waits for all data → renders complete HTML → flushes → ~1500ms TTFB.

With streaming + Suspense:

  1. Server flushes the shell + skeletons (~50ms TTFB).
  2. As each <Suspense> boundary’s data resolves, server flushes that section’s HTML.
  3. Client renders progressively as chunks arrive.
// React + Next.js App Router
<Suspense fallback={<HeaderSkeleton />}>
  <Header />
</Suspense>
<Suspense fallback={<FeedSkeleton />}>
  <Feed />     {/* slow data */}
</Suspense>

Result: user sees the shell immediately, sections fill in as they load. Best perceived LCP for data-heavy pages.

See 04_streaming_ssr_with_suspense.md.

Q: How do you pick per route?

A: Decision flow:

Is data per-user / behind auth?
  ├── Yes
  │    └── Is SEO needed? → No → CSR or SPA
  │                      → Yes → SSR (per-request render)
  └── No
       └── Does data change frequently (sub-minute)?
            ├── Yes → SSR or streaming SSR
            └── No
                 └── Does it change at all between builds?
                      ├── Yes (hourly/daily) → ISR
                      └── No (or rebuild on change) → SSG

Streaming SSR is the modern upgrade to SSR — same trade-offs, better perceived perf.

Q: What about CDN edge rendering?

A: SSR / ISR rendered at the CDN edge (close to the user) instead of a central data center. Lower latency, but limited compute (no full Node), often constrained DB access.

Platforms: Vercel Edge Functions, Cloudflare Workers, Netlify Edge, AWS Lambda@Edge. See 07_edge_rendering.md.

Use for: lightweight SSR (read-heavy, simple compute), geo-personalized content, A/B testing at the edge.

Q: Where does state live in each mode?

A:

Mode Server state Client state
CSR none (JS fetches from API) full client state
SSR rendered into HTML + serialized for hydration hydrates from serialized payload
SSG baked into HTML at build hydrates from baked data
ISR rendered into HTML + revalidated hydrates from current snapshot
Streaming streamed into HTML + serialized hydration data inline hydrates from streamed payload
RSC RSC payload (separate format) client components hydrate

The senior insight: server-rendered modes “serialize and hydrate” — the data fetched on the server is sent as JSON-ish payload alongside HTML so the client doesn’t refetch. Frameworks (Next.js, Nuxt, Remix) handle this automatically; understanding the mechanism explains why hydration cost is real.

Q: When does a hybrid app become necessary?

A: Almost always. Real-world: marketing pages (SSG/ISR), blog (ISR), product pages (SSR or ISR), dashboard (RSC), admin (SPA/CSR), API routes (server functions). One mode forced everywhere → wrong trade-offs on most routes.

Next.js App Router and Nuxt 3 make this per-route choice trivial. SPA-only stacks (CRA, plain Vite) need additional infra to serve different routes differently.

Gotchas / edge cases

  • CSR + SEO — Google generally executes JS, but other crawlers may not. Don’t rely on it for critical SEO routes.
  • SSR + cold starts — serverless platforms can have 100ms-2s cold starts. Edge functions are faster but more limited.
  • SSG with millions of pages — build time blows up. Use ISR with on-demand revalidation, or split builds.
  • ISR cache stampede — first request after revalidation triggers regeneration; concurrent requests during regen get the stale version (usually) or the new one (varies by platform).
  • Hydration mismatch between SSR/SSG output and client render — see ../05_react/hydration.md.
  • Streaming requires HTTP/1.1+ and a server stack that doesn’t buffer (nginx proxy_buffering off etc.).
  • Personalization on SSG — workarounds: edge-injected user data, client-side personalization layer on top of static base, or move to SSR/ISR.

What a senior is expected to say

  • “No single right mode — pick per route. Marketing pages SSG/ISR; per-user routes SSR or RSC; admin tools can stay SPA; the hybrid is the realistic answer.”
  • “CSR is fine when SEO doesn’t matter and audience tolerates load wait — admin tools, behind-auth dashboards. Bad LCP, bad SEO.”
  • “SSR fresh + SEO + slow TTFB + server cost. SSG fast + cheap + stale. ISR is the SWR-at-page-level hybrid.”
  • “Streaming SSR is the modern upgrade — shell streams immediately, suspended boundaries fill in. Best perceived LCP for data-heavy pages.”
  • “Server-rendered modes serialize fetched data into the HTML so the client doesn’t refetch on hydration. That’s why hydration cost is real.”
  • “Next.js App Router and Nuxt 3 are the production-ready hybrids today. SPA-only stacks are a hard constraint, not a choice.”

Cross-references

Further reading