Rendering on the Web - The Three Knobs Behind Every Paradigm

Written on: May, 2026

7 min read

CSR, SSR, SSG, ISR, RSC. Release posts treat each one like a new religion. They are not. They are coordinates on the same three knobs: where HTML is built, when it is built, and what crosses the wire. Learn the knobs and the acronyms stop scaring you.

Steps

  1. The three knobs (and how we got here in one lap)
  2. Knob one: where does the UI get built (server, build machine, or browser)? Knob two: when (every request, once at build, first hit then cache, or different answers per chunk)? Knob three: what actually crosses the wire (plain HTML, HTML plus a fat JS bundle, or a serialized component tree with client islands)? Every named paradigm is just a point in that space. The industry did not invent seven unrelated ideas; it kept moving those coordinates when networks, crawlers, and product expectations moved. Classic server templates answered per request with HTML. Static generators moved the work to build time so a CDN could serve cold files. SPAs pushed rendering into the browser and shipped an empty shell plus a bundle. SSR plus hydration brought HTML back for first paint but still paid the SPA tax twice. ISR and friends cached HTML so most hits stayed CDN-cheap. Streaming let fast chunks leave the server while slow ones caught up. Server Components finally changed the payload shape so not every server-rendered subtree had to arrive as executable JS in the client. The table below lists the coordinates explicitly; the next sections zoom in on three moments where the tradeoffs hurt enough that teams changed how they shipped.

  3. CSR: the browser owns the page
  4. Move all rendering to the client and the first response is honest: almost no HTML, almost all responsibility deferred to a bundle. Interactivity and client-side routing feel great once the app is warm. The cost is the gap between first byte and usable UI (download, parse, execute), plus SEO and low-end devices paying again on every navigation. Coordinate: browser, runtime in the browser, empty shell plus JS bundle.

    1<!-- Typical CSR first response: the page is a promise, not a document -->
    2<!DOCTYPE html>
    3<html>
    4  <head><title>My App</title></head>
    5  <body>
    6    <div id="root"></div>
    7    <script src="/bundle.js"></script>
    8  </body>
    9</html>
  5. SSR with hydration: fast paint, delayed hands
  6. Ship HTML from the server so something renders immediately, then send the same component graph again as JavaScript so clicks and state work. Users see layout early; they do not get a trustworthy interactive surface until hydration finishes. You still ship the SPA-sized bundle; you added server render cost on top. People call it shipping the page twice because that is what the runtime does. Coordinate: server then browser, usually every request unless you layer caching, HTML plus bundle.

    1// Hydration on a mid-range phone (illustrative, not a benchmark)
    2t = 0ms     →  request leaves the browser
    3t = 20ms    →  HTML arrives, paint happens
    4t = 400ms   →  bundle download completes
    5t = 600ms   →  hydration completes; buttons start working
    6
    7// Between paint and hydration: visible, not reliably interactive.
    1GET /dashboard
    2  ↳ <html>...server-rendered markup...</html>
    3  ↳ <script src="/bundle.js"></script>   // same tree, second pass
  7. RSC: the third knob actually moves
  8. For a long time the payload stayed “HTML, maybe with a bundle beside it.” Server Components change what crosses the wire: a serialized tree where server-run subtrees contribute rendered output without shipping their component implementation to the client, and client components show up as islands that still hydrate from the bundle. The upside is less JS for read-mostly UI; the downside is a stricter split between server and client components and more design work at the boundary. Coordinate: server plus browser, often per chunk or flight, serialized tree plus client islands. Real wire formats differ by framework; the JSON sketch is intentionally schematic.

    1// Schematic RSC-style payload (illustrative, not a literal network capture)
    2{
    3  "tree": [
    4    {
    5      "type": "div",
    6      "props": { "class": "page" },
    7      "children": [
    8        {
    9          "kind": "rendered-output",
    10          "html": "<h1>Welcome back, Aldi</h1>"
    11        },
    12        {
    13          "kind": "client-component",
    14          "name": "InteractiveCart",
    15          "props": { "items": [{ "id": 1, "name": "Mochi" }] }
    16        },
    17        {
    18          "kind": "rendered-output",
    19          "html": "<footer>© 2026</footer>"
    20        }
    21      ]
    22    }
    23  ]
    24}
    25
    26// Outer sections ship as output; the cart is the island that still pulls code.
  9. Knob sheet and a blunt way to choose
  10. Use the grid as the source of truth. When someone asks “should we use X,” translate X into where/when/what, then check whether that matches data freshness, personalization, and how much client JS you can justify. Heuristics without nuance: if it can be fixed at build time, prefer SSG; if it changes on a schedule or after publish events, cache rendered HTML (ISR-shaped); if it is per-user or must be fresh every request, render on the server; if one slow fragment blocks the rest, stream; if you need dense server UI with small interactive pockets, look at RSC-style splits. Marketing names change; the knobs do not.

    1Paradigm                   | Where                  | When                       | What crosses the wire
    2---------------------------+------------------------+----------------------------+-----------------------------------------
    3Classic server-rendered    | server                 | every request              | HTML
    4SSG                        | build machine          | once at build              | HTML
    5SPA / CSR                  | browser                | in-browser at runtime      | empty shell + JS bundle
    6SSR + hydration            | server, then browser   | every request              | HTML + JS bundle
    7ISR                        | server                 | first request, then cached | HTML (cached)
    8Streaming SSR              | server                 | per chunk                  | HTML, streamed
    9RSC                        | server + browser       | per chunk                  | serialized component tree + client islands

Conclusion

That is the whole map: where, when, what. New buzzwords will keep appearing because hardware and business constraints move; the control surface does not. When the next acronym lands, ask which knob moved and whether your product actually sits at that coordinate. If someone corners you about RSC, “it is the option that changes the payload, not just where and when HTML shows up” is enough to sound dangerously informed.
Back to Home
Aldi Krasniqi