skip to content

In a server-rendered route, what is hydration, and what must the browser be handed for it to happen?

level: juniorimportance: must knowfreq 80%

answer

  1. it paints before it works
  2. the second delivery of the same route
  3. same information travels twice
  4. code plus state, matched to existing markup
  5. attach behaviour, do not rebuild nodes

basics

~20 s

Hydration is a server-rendered route's second delivery: after the finished HTML paints, the browser downloads the component code and the data the server rendered with, then attaches behaviour to the existing markup instead of rebuilding it.

solid answer

~50 s

A server-rendered route arrives as finished HTML, so it paints and reads correctly before any application JavaScript runs — but nothing scripted works yet. Hydration is the step that makes that existing markup operable: the client bundle loads, renders the same component tree the server rendered, matches it against the nodes already in the document, and attaches state and event handlers to them rather than creating new nodes. For that to succeed the browser needs three things: the component code for whatever must become interactive, the same data the server rendered with (usually embedded in the response so the client need not re-fetch it), and enough structure to line its tree up with the markup. That is why the route effectively ships the same information twice — once as rendered HTML, once in machine-readable form for the takeover.

go deeper

for a junior

Be able to say it in one breath: the server sends finished HTML, the browser paints it, then script arrives and takes the existing markup over. Remember that the page is readable before it is clickable.

for a middle

Explain the three inputs the takeover needs — component code, the data the server rendered with, and a way to match tree to markup — and why reusing the painted nodes beats re-rendering them.

for a senior

Talk about the window between paint and takeover as something users actually hit: controls that look ready and are not, and main-thread work competing with the first interaction. Know how to read the per-route script cost.

for a principal

Frame hydration as a recurring bill on every server-rendered route and decide how much of it the product should carry, weighing interactivity against payload, resilience when script fails, and the authoring discipline each choice imposes.

## What hydration actually is A route rendered on the server reaches the browser as **finished HTML**: the markup is in its final shape and the text is the real text, so the browser parses and paints it without running any of the application's own JavaScript. That page is **readable but not operable**. Everything the application defines in script — the handler behind a button, the state a widget keeps, client-side navigation that avoids a full document load — lives in code that has not arrived yet. **Hydration** is the step that closes that gap. The client code loads, renders the same component tree the server already rendered, walks that tree against the nodes already sitting in the document, and **attaches behaviour to those nodes instead of creating new ones**. Nothing visible is supposed to change; what changes is that the page starts responding. The defining property of this leaf is that **the same information travels twice**. The values the server rendered are in the HTML as formatted text, and they are sent again in a machine-readable form the client code can branch on. The route is not usable until that second delivery lands *and* runs. ## What the client must be handed to take over - **The component code for whatever will be interactive.** HTML describes the result of a render, not the logic that produced it, so the logic ships separately. - **The data the render depended on.** A price string in the markup is not the number, the currency and the stock flag the component branched on. Frameworks normally embed that state in the response; the alternative is for the client to re-fetch it, which delays takeover and can produce a different answer than the server got. - **A way to line its tree up with the markup.** The client render has to know which node corresponds to which component before it can attach a handler — which is also why a framework must detect the case where its first client render and the server's HTML disagree *before* attaching anything, and fall back to re-rendering the affected part in the browser. ## Why the DOM is reused rather than rebuilt Throwing away the server's markup and rendering fresh would be simpler, and some setups effectively do that. It costs more: discarding painted content risks a visible flash, loses any scroll position or text selection the user already had, and repeats layout work the browser has finished. Reusing the nodes keeps the paint the user is already looking at and pays only for the client-side render plus the attachment work. That work is real even though no new DOM is produced. Hydration executes component code, rebuilds the framework's in-memory representation of the tree, and registers listeners — main-thread JavaScript that competes with the user's first clicks. ## What the user can do at each stage | Stage | What is in the document | What the user can do | |---|---|---| | HTML received, no script yet | Final markup, real text, CSS applied | Read, scroll, follow links, submit a plain form | | Script downloading | Same markup | The same as above; scripted controls look ready but are inert | | Takeover running | Same markup, listeners being attached | Mostly the same; the main thread may be busy | | Hydrated | Markup plus live state and handlers | Everything the application defines | The middle rows are the practical cost: a control can *look* finished long before it works, which is why the gap between paint and takeover matters more than either number alone. ## Where frameworks differ Meta-frameworks disagree about how much of this a route pays for. Some hydrate the whole route tree on load; some hydrate only the parts declared interactive and leave the rest as inert HTML; some defer the work until the browser is idle or a component scrolls into view; some serialise enough state to resume where the server stopped instead of re-executing the render; and some ship no client script for routes that need none. The mechanism is the same in each case — hand the client code plus state, attach to existing markup — but the amount handed over, and therefore the cost, differs a great deal. ## Reading it as a budget Treat hydration as a **per-route number, not a framework feature**: 1. Ask what script a given route downloads before it is interactive, not what the application ships in total. 2. Separate the shared runtime every route pays from the code this route's own interactive parts add. 3. Compare that to how much of the route is genuinely interactive; a reading-only page paying an application-sized takeover is the signal that scope is wider than the page needs. Rendering on the server buys a fast, meaningful first paint and markup a crawler can read. Hydration is the bill for making that paint interactive, and the scope you hydrate is the part you control.

  • Why does the client need the data the server rendered with, rather than deriving it from the HTML?
    The HTML holds formatted output, not the values components branch on: a rendered date string is not a timestamp, and a rendered row is not the record behind it. Without that state the client would have to re-fetch before it can render, which delays takeover and can return a different answer than the server saw.
  • What can a user do on a server-rendered page that has painted but not yet hydrated?
    Everything the platform handles without application script: read the content, scroll, follow links, and submit a form that posts normally. Controls whose behaviour lives in JavaScript are inert, even though they look finished. Whether input arriving in that window is queued and replayed afterwards depends on the UI library underneath.
  • Does hydration make the page re-render visually?
    It should not. The client render is compared against the markup already present and behaviour is attached to it, so the user sees no change. A visible change means the client produced a different tree than the server did, and the framework fell back to re-rendering that part in the browser.

Like a showroom display that arrives already assembled with a separate box of electrics: the shelf looks finished the moment it is unpacked, but nothing switches on until the second box turns up and someone wires it in.

saying these in an interview costs you the question

  • Thinks the browser throws the server HTML away and renders from scratch
  • Assumes server-rendered HTML alone makes the page interactive
  • Says hydration removes the need to ship component code
  • Ignores that the same values are sent as markup and as state
  • Treats hydration as download cost only, forgetting the main-thread work
  • Believes a painted control is a working control