Skip to content
Rendering Strategies (CSR · SSR · SSG · ISR · RSC · Streaming · Islands)

Building UIs

Listen 0%
Speed

07 · Rendering Strategies (CSR · SSR · SSG · ISR · RSC · Streaming · Islands)

Where and when your UI’s HTML gets generated — on the server at build time, on the server per request, on the client, or some hybrid. This is the highest-leverage architectural decision for performance and SEO, and the source of the most confusion. Connects the browser pipeline (01) to React (05) and Next (08).


Positioning

“Rendering strategy” answers two orthogonal questions: when is HTML produced (build / request / runtime-in-browser) and how much JS ships to make it interactive. Every modern framework is a set of knobs on these two axes. Get the vocabulary precise and the tradeoffs become obvious.


Foundations: the axes

  • Render time: build-time (SSG) · request-time on server (SSR) · runtime in browser (CSR) · revalidated (ISR).
  • Interactivity model: ship-everything-and-hydrate (classic SPA/SSR) · ship-nothing-extra (static) · ship-only-interactive-bits (islands / RSC).
  • Two metrics they trade against: TTFB / FCP / LCP (how fast users see content) vs TTI / INP (how fast they can interact). SSR improves the first; naive hydration can hurt the second.

Deep dive

1. CSR — Client-Side Rendering (the classic SPA)

The server sends a near-empty HTML shell + a JS bundle; the browser downloads, parses, executes, fetches data, and renders.

  • Pros: simplest hosting (static files + API), rich app-like interactivity, cheap to scale (CDN).
  • Cons: blank screen until JS loads & runs (bad LCP on slow devices/networks), SEO challenges (crawlers may not run JS well), large bundles.
  • Use for: authenticated dashboards, internal tools, highly interactive apps where SEO/first-paint don’t matter.

2. SSR — Server-Side Rendering (per request)

The server runs the components for each request, returns full HTML, then the client hydrates (attaches event listeners and reconstructs React state on top of the server HTML).

  • Pros: fast first paint, SEO-friendly, personalized per request (auth, geo).
  • Cons: server cost per request; hydration is expensive — the client re-runs much of the render to “adopt” the DOM, and until hydration completes the page looks ready but isn’t interactive (the “uncanny valley” of SSR).
  • Use for: content that’s both dynamic/personalized and needs fast first paint or SEO.

3. SSG — Static Site Generation (build time)

HTML is generated once at build, served from a CDN.

  • Pros: fastest possible (pre-rendered, edge-cached), cheap, secure (no server at request time), great SEO.
  • Cons: content is frozen at build; rebuilds needed for changes; build time grows with page count.
  • Use for: marketing sites, docs, blogs, anything that changes infrequently.

4. ISR — Incremental Static Regeneration

SSG + a TTL: pages are static but revalidated in the background after N seconds (or on-demand via a webhook/revalidateTag). The first request after expiry serves stale, triggers a regeneration, and subsequent requests get fresh.

  • Pros: static speed with fresh-ish content; no full rebuilds; scales to millions of pages.
  • Cons: brief staleness window; cache-invalidation reasoning.
  • Use for: large catalogs, e-commerce product pages, news — content that updates but not per-request.

5. Streaming SSR + Suspense

Instead of waiting for the entire page to render server-side before sending anything, the server streams HTML in chunks. Fast parts (shell, nav) flush immediately; slow parts (a data-heavy widget) stream in as they resolve, with a <Suspense> fallback shown meanwhile. React’s renderToPipeableStream/renderToReadableStream + selective hydration let the page become interactive piece by piece and prioritize hydrating whatever the user interacts with first.

  • Wins: much better TTFB and perceived performance; no “all or nothing” wait on the slowest query.
  • This is the substrate Next’s App Router and RSC build on.

6. RSC — React Server Components (the 2026 default in Next)

A new split (detailed in 05): Server Components render only on the server and ship zero JS; Client Components ('use client') ship JS and hydrate. The server emits a serialized RSC payload (not HTML — a description of the tree), streamed to the client, interleaved with the HTML.

  • Wins: dramatically less client JS (often 30–50% smaller payloads), data fetching co-located on the server (no client-side waterfalls, direct DB access), automatic code-splitting at the client boundary.
  • Model shift: “server-first by default, opt into client interactivity.” Most of your tree is server-rendered and never hydrated; only the interactive leaves are client components. This collapses much of the CSR/SSR/data-fetching distinction into one model.
  • Constraints/risks: server components can’t use state/effects/browser APIs; the boundary discipline ('use client'/'use server') is the new skill; serialization rules (you can pass serializable props across the boundary, not functions/class instances). Keep frameworks patched — the RSC protocol had critical 2025 CVEs (17).

7. Islands architecture

Ship a mostly-static HTML page with small islands of interactivity that hydrate independently (and lazily). Popularized by Astro; conceptually similar to RSC’s “only interactive bits ship JS.” Variants: partial hydration (hydrate some components), progressive hydration (hydrate on visibility/idle), lazy hydration.

  • Wins: minimal JS, excellent for content sites with sprinkles of interactivity.
  • Astro is framework-agnostic (React/Vue/Svelte islands on one page).

8. Resumability (the post-hydration idea)

Qwik pioneered resumability: instead of re-executing the app on the client to “hydrate,” it serializes the framework’s state into the HTML so the client can resume exactly where the server left off, downloading handler code only on interaction. This targets hydration’s core cost (re-running render on the client). Niche today but conceptually important — it’s where the “hydration is wasteful” critique leads.

9. The cost of hydration (why all this churn exists)

Classic SSR’s dirty secret: you do the work twice — once on the server to produce HTML, once on the client to make it interactive. For large pages this re-execution is a major INP/TTI cost and the motivation behind RSC (don’t ship/hydrate server-only parts), islands (hydrate only islands), and resumability (don’t re-execute at all). Understanding this explains the entire 2020s rendering-architecture arc.


The decision tree

Need SEO or fast first paint?
├─ No  → CSR (SPA) is fine; simplest model. (dashboards, internal tools)
└─ Yes →
     Does content change per request / per user?
     ├─ No, rarely changes          → SSG (marketing, docs, blog)
     ├─ Changes but not per-request → ISR (catalogs, product pages, news)
     └─ Yes, dynamic/personalized   → SSR / streaming SSR
                                       └─ Using React + want minimal JS → RSC (server-first; Next App Router)
Mostly static with interactive sprinkles? → Islands (Astro)
Hydration cost dominating?               → RSC / islands / resumability (Qwik)

Real apps mix strategies per route: a static marketing homepage (SSG), ISR product pages, an SSR/RSC dashboard, and a CSR settings panel — all in one app. Next.js makes this per-route choice the default workflow (08).


Worked example: choosing for an e-commerce site

  • Home / landing — SSG (or ISR with long TTL): rarely changes, must be instant and SEO-perfect.
  • Category & product pages — ISR (short TTL) or RSC with cached fetches: thousands of pages, prices/stock change, but not per-user; static speed + freshness.
  • Cart / checkout — SSR/RSC client components: personalized, interactive, can’t be static.
  • Account dashboard — CSR or RSC with client islands: behind auth, SEO irrelevant, app-like.

One site, four strategies, each matched to the content’s freshness and interactivity needs.


Pitfalls & gotchas

  • Hydration mismatches — server and client render different HTML (e.g., Date.now(), window checks, locale, random IDs). React warns and may discard server HTML. Use useId, stable data, and suppressHydrationWarning sparingly.
  • Treating SSR as a perf silver bullet — it improves first paint but can worsen INP via hydration if you ship a huge bundle. Measure TTI/INP, not just LCP.
  • Suspense/data waterfalls — sequential dependent fetches in nested components serialize; hoist and parallelize.
  • Over-using 'use client' — pushing the boundary too high re-clientifies the tree and loses RSC’s bundle wins. Keep client components as leaves.
  • ISR staleness assumptions — know your revalidation window and on-demand invalidation story.
  • Personalization in static output — leaking one user’s data into a cached page; be deliberate about what’s cacheable.

Interview / self-test questions

  1. Define CSR, SSR, SSG, ISR in one sentence each and give a use case.
  2. What is hydration and why is it expensive? (Client re-executes render to adopt server HTML and attach handlers.)
  3. How does streaming SSR + selective hydration improve perceived performance?
  4. What do RSC change about where code runs and how much JS ships?
  5. Islands vs RSC — same goal, different mechanism; compare.
  6. What is resumability and what problem does it attack? (Hydration re-execution; Qwik.)
  7. Name three causes of hydration mismatches and how to avoid them.
  8. Why might SSR hurt INP even while improving LCP?
  9. How would you mix strategies across routes of one app?

Recommendations

  • Choose per route based on freshness × interactivity × SEO; don’t pick one global strategy.
  • Default to server-first (RSC) in new Next apps; push 'use client' down to interactive leaves.
  • Prefer streaming + Suspense to monolithic SSR; parallelize data fetching.
  • Use ISR/on-demand revalidation for large, semi-dynamic catalogs.
  • Watch INP/TTI, not just LCP — minimize hydrated JS.
  • Keep server/client output deterministic to avoid hydration mismatches.

Books & references

  • Patterns.dev (patterns.dev, by Lydia Hallie & Addy Osmani) — the best free resource on rendering patterns (CSR/SSR/SSG/ISR/islands/progressive hydration) with diagrams.
  • react.dev — Suspense, renderToPipeableStream, RSC docs.
  • Next.js docs — the App Router rendering chapter is the practical reference (08).
  • Astro docs — islands architecture explained by its main proponent.
  • “Rendering on the Web” — Jason Miller & Addy Osmani (web.dev) — the canonical taxonomy article.
  • Qwik docs / “Resumable vs Replayable” — for the resumability argument.

Connections

Frontend Deep-Dive Library · content is the single source of truth.