skip to content

Hydration

Hydration attaches React to server-produced DOM instead of building it, which is why it is cheaper than a client render but far from free. Expect follow-ups on what happens to a click that lands before the bundle finishes loading.

part ofReactoverview, primer and where to startread it →
on this pageshow

explore

questions

5

A React app serves server-rendered HTML, then the JavaScript bundle loads and calls hydrateRoot. What does React actually do to that existing HTML during hydration, and what does it deliberately not do?

level: juniorimportance: must knowfreq 70%

answer

  1. the DOM already exists
  2. what is missing is behaviour, not markup
  3. listeners land on the root container
  4. state initializers run on the client too
  5. effects have never run before this point

basics

~20 s

Hydration reuses the server's DOM instead of rebuilding it: React renders the component tree on the client, matches it against the existing nodes, attaches event listeners at the root container, fills refs, then commits and runs effects.

solid answer

~50 s

Hydration is React attaching itself to HTML the server already produced, instead of creating that HTML again. When `hydrateRoot` runs, React renders the tree on the client exactly as it would for a fresh render — every component function executes, `useState` initializers run, `useMemo` computes, context is read — but instead of creating DOM nodes it walks the nodes already in the container and claims them. So the browser never re-parses or re-paints that markup, and there is no flash. React then attaches its event listeners on the root container, populates refs with the adopted nodes, commits, and runs effects, which never ran on the server. What it deliberately does not do is rebuild the DOM. That is the whole point: the DOM work is skipped, but none of the JavaScript work is, which is why hydration is cheaper than a client render and still far from free.

go deeper

for a junior

Be able to say in one sentence that hydration attaches React to HTML the server already sent, rather than creating that HTML again, and that the DOM nodes are reused.

for a middle

Explain the mechanics: components run on the client, hook initializers execute, React claims existing nodes instead of creating them, listeners go on the root container, and effects run after the commit.

for a senior

Show the cost model. Argue why a page can paint fast and still be unresponsive, and connect hydration time to the size of the client tree rather than to what is visible.

for a principal

Own the tradeoff of the model itself: hydration buys early pixels at the price of duplicated render work, and you should be able to say when that trade stops paying and what you would measure to know.

## The problem hydration solves Server-side rendering sends the browser a finished HTML document, so the user sees real content before any of your JavaScript has run. That markup is inert: there are no event listeners on it, no component state, and no React tree in memory. Hydration is the step that turns inert server markup into a running React application without throwing the markup away. ## What React actually does The entry point is a single call: ```javascript import { hydrateRoot } from 'react-dom/client'; import App from './App'; hydrateRoot(document.getElementById('root'), <App />); ``` From there, four things happen. **1. Your components run on the client.** React cannot infer a component tree from HTML — HTML has `<div>`s, not `<ProductCard>`s. So React performs a full render: every component function is called, every `useState` and `useReducer` initializer runs, `useMemo` bodies execute, context is read. The result is a complete in-memory tree describing what the UI should be. **2. Instead of creating DOM, React claims DOM.** During a client render React would call `document.createElement` for each host element. During hydration it instead walks the existing children of the container in render order and associates each element of the tree with the node the server already produced. Nothing is inserted, removed, or re-parsed, so the browser does no additional layout or paint for that markup. **3. Interactivity is wired at the root.** React does not attach a listener to every button. It installs listeners once on the root container you passed to `hydrateRoot` and maps an incoming event back to the right component through the in-memory tree. That is why "making the page interactive" is mostly a matter of having that tree exist. **4. Commit and effects.** Refs are filled with the adopted DOM nodes, `useLayoutEffect` callbacks run before the browser paints the commit, and `useEffect` callbacks run after. Neither ever ran on the server, which is exactly why browser-only work belongs in an effect. ## What hydration does not do - **It does not recreate the DOM.** No flash, no re-paint of the server content, no loss of things the browser already did to those nodes. - **It does not skip your component code.** Hydration is a render; the component functions run again on the client. "Attach, don't rebuild" describes the DOM, not the JavaScript. - **It does not rewrite markup it has matched.** React assumes the client render agrees with the server HTML and does not re-apply the attributes it trusts. When the two disagree you get a hydration error instead. - **It does not run effects early.** Anything inside `useEffect` has never executed by the time the user first sees the page. ## Cheaper, but not free The cost model is worth saying out loud in an interview, because it explains a class of production complaints. Hydration removes DOM construction from the critical path. It removes nothing else: the bundle still has to download, parse and execute; every component function still runs; the tree still gets built; effects still fire. All of that is main-thread JavaScript that happens *after* the user can already see the page. That is the familiar "looks ready, does nothing" window. Its size scales with how much client component tree you have, not with how much of the page is on screen — a huge tree below the fold costs the same as one in the viewport. It is also why a server-rendered page can score well on paint metrics and badly on interaction ones. ## Interview framing Lead with the one-sentence version — React renders on the client and adopts the server's DOM instead of creating its own — then immediately show you know the cost: the DOM work is skipped, the JavaScript work is not. Candidates who stop at "hydration makes the page interactive" sound like they have read a blog post; candidates who name what still runs sound like they have debugged one.

  • Do useEffect callbacks run during hydration, and when exactly?
    Yes, and only then — they never ran on the server. Hydration ends in a commit just like a client render, so `useLayoutEffect` runs before the browser paints that commit and `useEffect` runs after it. That is why browser-only work (measuring, subscribing, reading `window`) belongs in an effect rather than in render.
  • If hydration skips the DOM work, why can it still leave a page unresponsive for seconds?
    Because everything else is still on the main thread: downloading and parsing the bundle, calling every component function, running hook initializers, building the tree, and firing effects. That work scales with the size of the client component tree, not with what the user can see, so a large app pays a large bill after paint.
  • Does hydration re-run the work the server did to produce the HTML?
    It re-runs client component functions, so anything computed inline in render is computed again on the client. It does not re-execute server components — their output reaches the client as data, not as code to run — and it does not re-issue whatever requests the server made unless your own client code asks for them.

It is like moving into a house that is already built rather than building one: the walls and furniture are in place, but you still have to walk every room to find the light switches before anything actually turns on.

saying these in an interview costs you the question

  • Says React rebuilds the DOM from scratch during hydration
  • Thinks hydration skips calling component functions on the client
  • Believes effects already ran on the server before hydration
  • Assumes the page is interactive the moment the HTML paints
  • Thinks React re-applies every attribute onto the server's nodes

context

open as a page

In React 19, what is the difference between calling hydrateRoot(container, <App />) and createRoot(container).render(<App />), and what goes wrong if you use createRoot on a container that already holds server-rendered HTML?

level: middleimportance: must knowfreq 58%

basics

~10 s

createRoot renders a fresh tree into a container; hydrateRoot adopts server-rendered HTML already inside it, reusing those nodes. Calling createRoot on server HTML discards that markup and re-renders from scratch, wasting the server work.

open as a page

What is the onRecoverableError option of React's hydrateRoot and createRoot, when does React call it, and what does "recoverable" mean for the page the user is looking at?

level: seniorimportance: should knowfreq 32%

basics

~20 s

onRecoverableError is an option of hydrateRoot and createRoot that React calls when it hit an error and recovered by itself — typically a hydration error it fixed by re-rendering that content on the client. The UI is correct, so wire it to your error reporter.

open as a page

On a server-rendered React 19 page, a user clicks a button before hydration has finished. What happens to that click, and what does React do about interactions that arrive during hydration?

level: seniorimportance: should knowfreq 46%

basics

~20 s

If hydrateRoot has already started, React captures the click on the root container, prioritizes hydrating the subtree the user touched, and replays the event once that subtree is ready. If the bundle has not executed yet, no listener exists and the click is lost.

open as a page

A server-rendered React page paints in under a second but stays unresponsive for several seconds afterwards while hydration runs. As the engineer who owns this page, how do you reason about hydration cost, and what tradeoffs do you weigh in reducing it?

level: principalimportance: should knowfreq 30%

basics

~20 s

Hydration cost scales with the size of the client component tree, not with what the user can see. Reduce it by keeping non-interactive parts out of that tree and splitting what remains so interactive regions hydrate first, then verify with real-user interaction data.

open as a page