Engrain by ByteRush

RCT.06.01 Mid-level· 12 minCore — asked often

CSR, SSR, SSG, ISR and streaming: choosing where pages are rendered

Client rendering vs server rendering vs static generation vs incremental regeneration, how each affects TTFB, FCP, LCP and INP, streaming SSR with Suspense, and selective hydration.

Comes up in: React, Frontend system design, Recruiter screen

Learn#

Tanya’s shop renders everything in the browser. Search engines see an empty <div id="root">, and phones show a blank screen for 3 seconds while JavaScript downloads.

Where HTML is produced changes what users wait for:

  • Client-side rendering (CSR): the server sends an empty shell; JavaScript builds the page.
  • Server-side rendering (SSR): the server builds the HTML for each request.
  • Static site generation (SSG): HTML is built once, at deploy time.
  • Incremental static regeneration (ISR): static pages rebuilt in the background after a set time.

With streaming SSR, the server sends HTML in pieces. Parts inside Suspense arrive when their data is ready:

import { createElement as h, Suspense, use } from "react";
import { renderToReadableStream } from "react-dom/server";

const wait = (ms) => new Promise((r) => setTimeout(r, ms));
const reviews = wait(100).then(() => ["Great fit", "Fast delivery"]);
function Reviews() { return h("ul", null, use(reviews).map((r) => h("li", { key: r }, r))); }
function ProductPage() {
  return h("main", null,
    h("h1", null, "Cotton kurta"),
    h(Suspense, { fallback: h("p", null, "Loading reviews…") }, h(Reviews)));
}
const start = Date.now();
const stream = await renderToReadableStream(h(ProductPage));
const decoder = new TextDecoder();
let n = 0;
for await (const chunk of stream) {
  const text = decoder.decode(chunk);
  const ms = Date.now() - start;
  n++;
  console.log(`chunk ${n} (${ms < 50 ? "at once" : "after the data"}):`,
    text.includes("<h1>") ? "page shell with h1 and the fallback" : "",
    text.includes("Great fit") ? "the reviews HTML plus a script to swap it in" : "");
}
This example uses Node.js, so it runs in our checks rather than in your browser.

Output (verified)

chunk 1 (at once): page shell with h1 and the fallback
chunk 2 (after the data):  the reviews HTML plus a script to swap it in
Streaming a product pageStep 1 of 4

State

  • render: shell ✓, reviews: waiting

Network

    empty

    Page

      empty

      The request arrives. The server renders the product page; the reviews are wrapped in Suspense and still loading.

      Read the steps as text
      1. The request arrives. The server renders the product page; the reviews are wrapped in Suspense and still loading.
      2. Chunk 1 is sent at once: the header, title, price and a "Loading reviews…" fallback. The browser can paint now.
      3. The reviews data arrives on the server. React renders that part and sends chunk 2: the HTML plus a tiny inline script.
      4. The script moves the reviews into place, replacing the fallback, even before the JavaScript bundle has loaded.

      Predict#

      Predict first

      An SSR product page shows the price at 0.8 s, but tapping 'Add to cart' does nothing until 3 s. Why?

      Interview angle#

      Say it like this — about 30 seconds

      “CSR sends a shell and renders in the browser: simple and good for logged-in apps, but slow first paint and weak SEO. SSR renders HTML per request: fast first paint and SEO, with server cost and hydration. SSG builds pages at deploy time, served from a CDN, ideal for content. ISR rebuilds static pages periodically. Streaming SSR with Suspense sends the shell first and fills slow parts in later, and selective hydration makes the parts users touch interactive first.”

      Stays on your device. Nothing is uploaded.
      Follow-up CSR vs SSR vs SSG: which would you choose for an e-commerce product page and why?
      ISR or SSG with short revalidation for the shared content (fast, cached, SEO), with price and stock streamed or fetched fresh; SSR if everything is personalized.
      Follow-up Explain streaming SSR with Suspense.
      The server sends the shell with fallbacks right away, then streams each Suspense boundary’s HTML with a small script that swaps it in when its data is ready.
      Follow-up What is selective hydration?
      React hydrates Suspense boundaries independently, and hydrates the one the user interacts with first.

      Practice#

      Guided

      Encode a defensible default for choosing a rendering strategy per page.

      Done when

      • Logged-in pages without SEO use CSR; personalized pages that need SEO use SSR.
      • Rarely changing pages use SSG, hourly ones ISR, per-request ones SSR.
      Your solution
      Ctrl/⌘ + Enter

      Remember#

      Try to answer each card before you open it. Then add them to your reviews.

      What is SSG?

      Pages rendered to HTML at build time and served as static files.

      What does streaming SSR send first?

      The page shell, with Suspense fallbacks for slow parts.

      Why can an SSR page look ready but ignore taps?

      It has not hydrated yet, so no event handlers are attached.

      What is ISR?

      Static pages regenerated in the background after a set time.

      Go deeper

      Four layers. Stop at the one your interview needs; each ends with a quick check.

      The core ideaEvery round
      SSG from a CDN gives the best TTFB and LCP. SSR adds server time to TTFB but improves FCP and LCP over CSR. Hydration cost hurts INP in all server-rendered setups (QLT.01.01).
      Check yourself: Which strategy gives the best TTFB for content pages?

      SSG served from a CDN.

      How it works underneathSenior rounds

      renderToReadableStream (web streams) and renderToPipeableStream (Node streams) render with Suspense support. Content for a boundary arrives in a later chunk with an inline script that moves it into place Source: React: renderToReadableStream.

      Check yourself: Which React API streams HTML in web-standard environments?

      renderToReadableStream (react-dom/server).

      Edge cases and trapsWhere people slip

      Once the first chunk is sent, the status code and headers are fixed. Decide 404s and redirects before the shell, and put slow, optional parts inside Suspense.

      Check yourself: Can the server change the HTTP status after streaming has started?

      No. Headers are sent with the first chunk, so decide the status before the shell.

      In productionStaff-level answers

      Selective hydration hydrates each Suspense boundary on its own and prioritizes the one the user clicks Source: React 18 working group: New Suspense SSR architecture.
      Frameworks such as Next.js let you choose the strategy per route.

      Check yourself: Which part does selective hydration hydrate first?

      The boundary the user is interacting with.

      Finished the five steps? Mark it, and its cards join your reviews.

      Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.

      Interview questions this answers

      • Explain streaming SSR with Suspense.
      • CSR vs SSR vs SSG: which would you choose for an e-commerce product page and why?