Library
00/07 · ~32 min
GUIDEDECK · where and when your HTML is built

Rendering
Strategies & the trade-offs
behind each one.

A 32-minute working session on how the web turns components and data into HTML — CSR, SSR, SSG, ISR, and streaming — and how to pick the right one per route instead of betting the whole app on a single default.

~32 MINBEGINNER → INTERMEDIATEFRAMEWORK-AWARE
SCROLL
01 · The rendering spectrum 4 min

Every strategy answers two
questions: where and when.

"Rendering" just means turning your components and data into the HTML a browser can paint. The only thing that really differs between strategies is where that work happens— a build machine, a request-time server, or the user's browser — and when it happens. Get those two axes straight and the rest of this deck is just named points on a map.

Rendering producing the HTML for a page from components plus data. It can run ahead of time (at build), per request (on a server), or in the browser(with JavaScript). Most real apps mix all three — the modern question isn't "which one?" but "which one for this route?"
BUILD TIME · ahead REQUEST TIME · per visit CLIENT · in the browser SSG prebuilt HTML served from CDN fastest · stale ISR prebuilt, then re-generated fast · fresher SSR HTML per request always current fresh · server cost Streaming shell now, data in chunks fast shell · async CSR browser builds it

One axis: HTML built early (SSG/ISR) is fast but can go stale; built late (SSR/CSR) is fresh but costs time or compute. Everything else is detail.

Build time

Render once when you deploy. The result is a static file a CDN can serve to millions for nearly free — but it's a snapshot.

Request time

Render fresh on a server for each visitor. Always current and personalizable — you pay compute and time on every hit.

Client time

Ship JavaScript and let the browser build the page. Cheap servers, rich interactivity — but a slow, blank first paint.

The real answer

Hybrid. Marketing page static, dashboard client-rendered, product page revalidated. Pick per route, not per app.

02 · Client-Side Rendering — the SPA 5 min

Ship an empty page, then let
JavaScript build the rest.

In the classic single-page app, the server sends almost no HTML — just an empty <div> and a script tag. The browser downloads the bundle, runs it, fetches data, and only then paints the screen. Brilliant for app-like interactivity; rough on that very first impression.

CSRClient-Side Rendering — the server returns a minimal HTML shell and a JavaScript bundle; the browser does all the rendering. This is the default for a plain React, Vue, or Angular SPA (single-page application) with no server framework around it.
<!-- what the server actually sends --> <body> <div id="root"></div> <!-- empty! --> <script src="/bundle.js"></script> </body> // then, in the browser, after the bundle loads: createRoot(root).render(<App />)
empty HTML download bundle.js run JS + fetch data blank screen — nothing to see yet first paint interactive right away

The user stares at a blank screen until the bundle downloads, runs, and fetches — then everything appears at once, fully interactive.

CSR wins when
  • The page sits behind a login — dashboards, editors, internal tools — where SEO and first paint barely matter.
  • Interactivity is the whole point: drag-and-drop, live canvases, heavy client state.
  • You want cheap, static hosting— the server only ships files; all the work is the user's CPU.
CSR hurts when
  • First impression matters — a blank screen while a big bundle loads on a mid-range phone over 4G.
  • SEO and link previews matter — crawlers and social scrapers may see an empty shell.
  • The bundle keeps growing — every feature you add taxes every visitor's load time.

Like mailing someone a flat-pack and the instructions: it travels light, but they can't use the furniture until they've built it themselves.

03 · Server-Side Rendering & hydration 5 min

Send real HTML first,
then wake it up.

SSR flips CSR around: the server runs your components per request, fetches the data, and sends finished HTML. The user sees content almost immediately. The catch is the second act — hydration — where the browser still has to download the same JS and re-attach all the interactivity.

SSRServer-Side Rendering — the server builds the full HTML for each request and sends it down, so the first paint shows real content. Time to first byte now depends on your server and data fetches — see Networking for why TTFB matters and how to shrink it.
Hydration the browser downloads the JS, re-builds the component tree, and attaches event handlers to the server-rendered HTML so it becomes interactive. Until hydration finishes the page looksready but buttons don't respond — the awkward gap often called "uncanny valley" or hydration lag.
server render + fetch HTML visible content painted but inert clicks do nothing hydrated fully interactive after JS downloads

Content paints early (good), but there's a window where it looks ready yet ignores clicks — until hydration finishes.

What SSR buys — and costs

  • Fast, meaningful first paint — real content, not a spinner. Great for content and commerce.
  • Reliable SEO & previews — crawlers get complete HTML without running your JS.
  • Butyou run a server for every request, and slow data pushes TTFB out — the page can't start until the server finishes.
  • Andyou still ship the JS for hydration — SSR doesn't make the bundle smaller, it changes when the user sees pixels.
Where Server Components fit. Modern React splits this further: Server Components render on the server and ship zero JS, while only interactive Client Components hydrate. That shrinks the hydration bill dramatically — the full mechanics live in Modern React. Here, just hold the idea: not every component has to hydrate.
04 · Static generation & incremental revalidation 5 min

Render it once, serve
it a million times.

If a page looks the same for everyone, why rebuild it on every request? SSG renders it at deploy time into a plain file a CDN serves instantly and almost for free. The only weakness is staleness — and ISR is the fix: keep the static speed, but let pages quietly refresh themselves.

SSGStatic Site Generation — render every page once at build time into static HTML. There is no server in the request path: a CDN serves the prebuilt file from an edge near the user. Fastest possible delivery — but the content is frozen until the next deploy.
ISR Incremental Static Regeneration — serve the static page, but re-render it in the background after a set interval (or on demand) so it stays fresh without a full rebuild. You get static speed with a controllable staleness window.
// Next.js App Router — ISR by time export const revalidate = 3600 // re-build at most hourly export default async function Page({ params }) { const post = await getPost(params.slug) return <Article data={post} /> } // or on-demand, after an edit lands: revalidateTag("posts") // purge just this content
build CDNstatic HTML users ✓ — after revalidate window — re-renderin background 1st hit user still gets a fast (slightly stale) page

ISR serves the cached page instantly, then regenerates it in the background — readers never wait on a rebuild.

SSG fits

Docs, marketing, blogs, changelogs — content that rarely changes and looks identical for everyone. Cheapest and fastest there is.

ISR fits

Catalogs, listings, news — mostly-static pages too numerous or too fast-moving to rebuild fully, where minutes of staleness are fine.

Watch for

Per-user or real-time data (cart, balance, dashboards) — static caching leaks one person's view to another. Keep those dynamic. On-demand revalidation pairs with cache tags.

Like a printed newspaper (SSG) versus one with a small inset that gets re-printed each morning (ISR) — far cheaper than a fresh edition per reader.

05 · Streaming, Suspense & partial prerendering 5 min

Send the fast parts now,
the slow parts as they finish.

Plain SSR has one flaw: the slowest piece of data holds the whole page hostage — nothing ships until everything is ready. Streaming breaks that deadlock. The server sends the instant parts immediately and flushes the slow ones as chunks, with Suspense drawing placeholders in the gaps.

Streaming SSR the server sends HTML in chunks as each part becomes ready, instead of buffering the whole page. Suspense boundaries mark async regions: React shows a fallback (a skeleton) immediately, then streams in the real content when its data resolves. The shell is interactive while slow widgets still load.
// the shell renders instantly; each slow part streams in export default function Dashboard() { return ( <Shell> <Suspense fallback={<Skeleton />}> <SlowRevenueChart /> // awaits a 2s query </Suspense> </Shell> ) }
static shell · instant nav · header footer <Suspense> dynamic hole skeleton → streamed content shell is interactive while the hole fills

The static shell paints and reacts instantly; the slow region shows a skeleton, then streams in real content when its data lands.

PPRPartial Prerendering — the natural endgame: a single route serves a static, CDN-cached shell with dynamic holes streamed inper request — combining SSG's instant first paint with SSR's freshness in one response. In Next.js 16 this is built on Cache Components and the use cachedirective: you mark what's cacheable, and everything else streams.
Streaming & PPR shine when
  • A page mixes fast and slow data — a cached header with a personalized, expensive widget.
  • You want the best first paint without blocking on the slowest query in the tree.
  • Built on Server Components and Suspense — the same primitives, now reused for delivery.
Mind the trade-offs
  • Streaming responses are harder to cache wholesale and to reason about than a single static file.
  • A skeleton that shifts when content lands hurts layout stability (CLS) — reserve the space.
  • Don't stream a page that's fully static anyway — you'd add complexity for zero gain.
06 · SEO, Core Web Vitals & cost 4 min

The same choice, scored on
visibility, speed, and bill.

Strategy isn't an aesthetic preference — it shows up in search rankings, in the metrics Google grades you on, and on your hosting invoice. Here's how the spectrum lands on each, and the frameworks that let you pick per route.

Core Web Vitals Google's field metrics for real user experience: LCP (Largest Contentful Paint — when the main content shows, good ≤ 2.5s), INP (Interaction to Next Paint — responsiveness, good ≤ 200ms; it replaced FID in 2024), and CLS (Cumulative Layout Shift — visual stability, good ≤ 0.1). They feed search ranking, so rendering choices have SEO consequences.
request TTFB server responds FCP first pixels LCP main content interactive INP responsive time →

SSG/ISR pull TTFB and LCP far left; CSR pushes first content right (blank, then a burst); SSR is early paint with a hydration gap before interactive.

SEO

Crawlers and social scrapers read HTML best. SSG/SSR/ISR hand them complete markup; pure CSR risks an empty shell — Googlebot can run JS, but on a budget and a delay. Static <title> and OG tags are safest.

Web Vitals

SSG/ISR win LCP (prebuilt, edge-served). SSR/streaming paint early but watch the hydration cost on INP. CSR tends to the worst LCP — nothing until the bundle runs.

Cost

Static is nearly free to serve at any scale. SSR/streaming bills compute on every request — and edge runtimes trade a constrained sandbox for lower latency than regional node.

Tooling landscape

Pick on how app-like the product is and which language your team lives in — each framework supports the whole spectrum, but optimizes for a different center of gravity.

Next.js (App Router)

Pro — every strategy in one place: Server Components, SSR, SSG, ISR, streaming, PPR.

Con — large surface area; richest features lean on Vercel.

Choose for React apps that need fine-grained, per-route control.

Astro

Pro — islands architecture ships zero JS by default; superb for content.

Con — less natural for heavily app-like, stateful UIs.

Choose for marketing, docs, and content-first sites.

SvelteKit

Pro — compiler output is tiny; pick SSR/SSG/CSR per route via adapters.

Con — smaller ecosystem and hiring pool than React.

Choose for lean, fast apps where bundle size is king.

Nuxt

Pro— Vue's answer: hybrid rendering rules set per route.

Con— Vue-only; you inherit that ecosystem's bounds.

Choose when your team already builds in Vue.

The same app renders differently depending on the runtime it lands on — edge is globally close but constrained; node is full-featured but regional.

Vercel

Pro — first-class Next.js: ISR, streaming, and PPR work out of the box, node or edge.

Con — usage-based pricing climbs with request volume.

Choose when Next.js is your framework and DX matters.

Cloudflare

Pro — Workers run at the edge with near-zero cold start; cheap at scale.

Con — constrained runtime; not full Node by default.

Choose for global low-latency SSR and edge logic.

Netlify

Pro — strong static + functions story; edge and node both available.

Con — advanced framework features can lag the platform they ship on.

Choose for static-heavy sites with some dynamic glue.

07 · Choosing per route + recap 4 min

Default to static. Earn
every step toward dynamic.

There is no "best" rendering strategy — only the cheapest one that meets a route's real freshness and interactivity needs. The win is realizing it's a per-route decision: one app can serve a static landing page, a revalidated catalog, and a client-rendered dashboard side by side.

static · cheap · fast dynamic · fresh · per-user SSG landing · docs blog · changelog ISR / PPR product · listing news feed SSR / stream search results personalized home CSR dashboard · editor behind auth

Walk left to right only as a route's freshness and per-user needs demand it — most routes stop sooner than you'd guess.

1Start static, go dynamic only when forced. SSG/ISR is the right answer far more often than its reputation suggests — most pages are the same for everyone.
2Match the strategy to freshness. Frozen content → SSG; minutes of staleness OK → ISR; must be current → SSR; per-user and interactive → CSR.
3Reserve CSR for behind-the-login interactivity.Where SEO and first paint don't matter, the SPA model is a fine fit — not a default for public pages.
4Stream to unblock the slow part, and let PPR fuse a static shell with dynamic holes — but only where a page genuinely mixes fast and slow data.
5Decide per route, not per app. The framework lets every page choose; spend your speed and compute budget where each page actually needs it.

A 60-second decision guide

  • Same for everyone & rarely changes? → SSG. Marketing, docs, blog posts.
  • Same for everyone but changes often / too many to rebuild? → ISR (or PPR for a static shell with a dynamic slice).
  • Must reflect right-now data or per-request personalization? → SSR, streaming the slow parts.
  • Highly interactive and behind a login?→ CSR, or SSR with client islands — SEO isn't in play.
  • Still unsure? Pick the more static option and add dynamism when a real need appears — not before.
Knowledge check

Did it stick?

Five quick questions on CSR, SSR, SSG/ISR, streaming, and Core Web Vitals — instant feedback, no sign-in.

Rate this deck
be the first

Navigate with ← → or scroll · back to library