Hydration and its cost
Visible HTML and working client behavior are separate milestones.
Predict first
Before you read: predict
A console page is delivered with a visible button and an application click handler supplied by a client component. A proctor clicks once before the client root is started, then the harness starts the matching root and waits for its readiness signal before a second click.
What cumulative application-handler counts does the fixture observe?
The 30-second version
Server HTML can show a component before client behavior is attached. Hydration runs the client tree against that snapshot and connects logic. Work depends on the hydrated tree and its code. Initial output must match the server snapshot; a mismatch can require recovery. A boundary can become ready; early clicks before root setup are not replayed.
Visible HTML and working client behavior are separate milestones.
How it works
Step 1 of 8
Analogy: A restored pipe organ
A restored pipe organ can have a polished facade and finished keys while the builder reconnects the mechanisms between keys and pipes. A visitor sees an instrument before every intended action is connected. Connecting additional stops adds work even when the facade looks unchanged. The comparison is between visible presentation and connected behavior.
| Organ restoration | Hydration |
|---|---|
| Finished facade | Visible server snapshot |
| Reconnecting key mechanisms | Attaching client behavior |
| More stops requiring work | A larger hydrated tree |
| Facade and mechanism disagree | Inconsistent server/client output |
The organ does not model event replay. Render versus commit belongs to react-render-commit; Server Component bundle boundaries belong to next-server-client-components; interaction latency belongs to perf-inp.
Examples, run for real
4.1 EXH-1600: appearance and activation are separate observations
The local page receives HTML from React's server renderer. Its harness controls when the matching client root begins and records application-handler activity around a pre-start and post-ready interaction. A plain link or native form may still have browser behavior independently of a client handler. A separate local control deliberately installs both the hydrated handler and an extra native listener; the asserted output is [2,4] after two clicks, showing that duplicated attachment changes application counts. A companion control starts with server HTML, waits for a client promise, and records a persistent focus event after release. hydrateRoot
MR !1800 keeps unrelated console widgets outside the owned client fragment. The fixture separates an interaction before root setup from a persistent event delivered to an already established, blocked boundary.
4.2 Catalog work without a stopwatch claim
The count fixture renders several catalog tree shapes with the same Row component and production React without StrictMode. Its independent worked tree contains three Rows and records worked_tree: 3. It also retains ordinary static spans next to a smaller owned island. The island itself includes two nested static children, so the observation distinguishes the selected client tree from nearby server-only HTML. A product page can therefore retain explanatory markup while hydrating only the control that needs behavior. This isolates ownership scope from network transfer, retry behavior and a claim about wall-clock time. Rendering on the web
The exercise does not rely on the bare statement that Server Components can ship no client JavaScript. It compares local client ownership scopes and the observed counts in the same document.
4.3 Replay and recovery are separate cases
The replay fixture begins with a completed server boundary. The client boundary waits on a harness-controlled promise while the root is already established; the fixture records the event log before and after release. A separate fixture compares a matching first render with two kinds of initial disagreement: changed text and a changed element kind. It records final content, node identity and recoverable-error presence. Selective hydration, hydration guidance
These examples are narrow. They do not promise attribute patching, universal replay, or a wall-clock cost.
Try it
Answer first, then check. Nothing is saved.
Question 1 of 5
A server button begins with one label, while the first client render supplies a different label. No warning suppression is used.
Which result does the fixture observe?
Question 2 of 5
A client root already exists around server HTML. A completed boundary waits for a local readiness promise. The existing control receives focus before that promise is released.
What does the fixture record after release?
Question 3 of 5
A root contains one owned Button island with two nested static children and seven static sibling Rows. The fixture retains all static markup.
If the owned island alone is hydrated, how many client component calls does it observe?
Question 4 of 5
Server HTML is <span id="control">Ready</span>. A candidate client tree first returns that matching span, then an effect returns <button id="control">Client</button>.
Which tuple does the fixture record: final text, original-node identity, recoverable error?
Question 5 of 5
A bootstrap helper calls root.render while a server-rendered boundary is still waiting on its client promise.
Which final pair does the fixture observe?
Go deeper
Pro tip
Keep the server snapshot and first client inputs together in a hydration bug report so a teammate can reproduce the same disagreement.
Sources
Fetched 2026-09-30 through the saved source transport: React hydrateRoot and React working group: Selective Hydration. P2 is executed-only because the saved official React reference does not key the narrower replay rule.
Related lessons
Checked against the tools and docs as of 2026-09-30.