Lesson 02

Rendering: where does HTML get built?

CSR, SSR, SSG, ISR, Streaming. Five flavors of the same question — “who builds the page, and when?”

1 · The 5 modes, in plain English

🧠 in plain english
Imagine ordering a custom T-shirt. 5 ways to get it printed:
CSR = you get a blank shirt, an iron-on, and instructions. You print it yourself at home. (Fast factory, slow at home.)
SSR = factory prints one fresh shirt per order, then ships. (Slow factory, fast wear.)
SSG = factory printed 1000 identical shirts last week, picks one off the shelf. (Instant ship, no personalization.)
ISR = SSG, but factory reprints stock every week. (Cheap and fresh.)
Streaming SSR = factory ships the box now with the shirt body, sleeves arrive 10 seconds later in a separate box. (See content immediately, full page assembles in pieces.)
ModeHTML builtBest forTradeoff
CSRin browser, after JS loadsauth dashboardsblank screen, bad SEO
SSRper request, on serverpersonalized + SEOTTFB blocks on slow query
SSGat build time, oncemarketing pagesstale until rebuild
ISRat build, regen on scheduleblogs, productsfirst user post-expiry pays cost
Streamingchunks via Suspensedata-heavy dashboardscaching is harder

2 · Race them — see the difference

Same hypothetical page rendered three ways. Watch when each paints (user sees something) vs when it becomes interactive (clicks work).

CSR
SSR
Streaming
  • CSR: blank screen until JS loads, then renders. SEO bad.
  • SSR: server holds response until ALL data ready, then flushes everything. Bad if any query is slow.
  • Streaming: server flushes shell immediately, slow data fills via Suspense. Best of both worlds.
Streaming wins for dashboards. User sees the header in 200ms while the wallet balance is still loading from the database. Old SSR would block the whole page on the slowest query.

3 · React Server Components in 60 seconds

🧠 in plain english
Imagine a Pixar movie. The backgrounds are pre-rendered (server). The characters need to move on screen (client). RSC = let the server pre-render the static parts and ship zero JavaScript for them. Only the interactive bits ship JS.
  • RSC components render only on server. They never reach the browser as JavaScript.
  • They can await a database call directly. No useEffect.
  • They can't use useState, useEffect, event handlers, or browser APIs.
  • "use client" is a boundary. Everything ABOVE = server. Everything BELOW = client.

Step through it line-by-line. Watch what runs on the server (left), what HTML streams to the browser (middle), and how the JS bundle stays at 0 KB until it hits a "use client" boundary:

React Server Components — what "use client" really does
tsx
step 0 / 10
1// app/dashboard/page.tsx — SERVER component (default)
2export default async function Dashboard() {
3 const balance = await db.wallet.find(); // ← runs on server only
4 return (
5 <div>
6 <h1>Wallet</h1>
7 <Balance value={balance} /> {/* server, 0 KB */}
8 <PlaceBetButton /> {/* CLIENT, 12 KB */}
9 </div>
10 );
11}
12
13// app/PlaceBetButton.tsx
14"use client"; // ← boundary: this and below ship JS to browser
15export function PlaceBetButton() {
16 const [pending, setPending] = useState(false);
17 return <button onClick={...}>Bet</button>;
18}
🖥️SERVER
💻CLIENT
no JS yet
HTML payload streamed to browser
empty
JS bundle shipped
0KB
RSC zone
press ▶ play to step through this code, or use ← → to scrub.

4 · Hydration — visible ≠ interactive

If you remember nothing else from this lesson, remember this. SSR is fast at painting pixels but slow at making them interactive. Watch the timeline — paint at 400ms, but the button doesn't actually work until ~1450ms because the JS bundle has to download, parse, and reconcile.

Hydration — why visible ≠ interactive
html
step 0 / 9
1// 1. Server renders HTML
2<html>
3 <body>
4 <h1>Wallet</h1>
5 <button>Place bet</button> <!-- not yet clickable -->
6 <script src="/bundle.js" defer></script>
7 </body>
8</html>
9
10// 2. Browser parses HTML, paints pixels (LCP fires)
11// 3. Browser downloads /bundle.js (~140 KB gzipped)
12// 4. JS executes — React.hydrateRoot(...)
13// walks the DOM tree, attaches event listeners, reconciles
14// 5. NOW the button works
⌛ blank screen — waiting on server
paint
JS bundle
0/140 KB
interactive
timeline
still loading
press ▶ play to step through this code, or use ← → to scrub.
📣 say this in the interview
We use streaming SSR with Suspense around expensive data, so the navigation and static content paint immediately. Wallet balance is its own boundary that resolves independently. TTFB stays low even when one query is slow.
⚡ quick check
A regulator demands that every page they audit returns its content in raw HTML, no JavaScript required. Which mode CAN'T satisfy this?

4 · PAM rendering recipe

AI tutor
llama-3.1-8b-instant
Ask anything about the interview
Concept clarifications, “explain X simpler,” rehearsal phrasing, follow-up questions to model answers.