sveltejs/svelte
Hydration
Active contributors: Rich Harris, Simon H, Dominic Gannaway
Purpose
Hydration is the process of pairing server-rendered HTML with the client-side reactivity graph so that subsequent updates apply to the existing DOM rather than re-creating it. Svelte 5's hydration is comment-based and walks the DOM tree linearly to keep alignment with the compiled component's emit order.
How it works
graph LR
SSR["render() on server<br/><code>internal/server/index.js</code>"] -->|HTML with<br/>HYDRATION_START / HYDRATION_END<br/>comment markers| HTML["server HTML"]
HTML -->|sent to browser| BROWSER["browser"]
BROWSER -->|user calls hydrate(...)| H["hydrate()<br/><code>internal/client/render.js</code>"]
H -->|set hydrating = true| W["walker<br/><code>internal/client/dom/hydration.js</code>"]
W -->|walks block markers| BL["block runtime fns<br/><code>internal/client/dom/blocks/*</code>"]
BL -->|attach reactivity to existing nodes| DOM["live DOM"]Comment markers
Both server and client agree on three comment strings, defined in packages/svelte/src/constants.js (and re-exported from packages/svelte/src/internal/server/hydration.js):
| Constant | Comment text | Meaning |
|---|---|---|
HYDRATION_START |
<!--[--> |
Start of a block. |
HYDRATION_END |
<!--]--> |
End of a block. |
HYDRATION_ERROR |
<!--[!--> |
Server emitted an error placeholder. |
EMPTY_COMMENT |
<!----> |
Spacer that lets the walker advance one position. |
BLOCK_OPEN |
<!--[--> (client view) |
Same as HYDRATION_START. |
BLOCK_OPEN_ELSE |
<!--[!--> |
Indicates the :else branch of an if/each. |
Whenever the compiled server output produces a block (an if, each, await, key, snippet render, etc.), it brackets it with these comments. The compiled client output for the same block calls next() and reset() from internal/client/dom/hydration.js to advance the walker and consume the markers.
Walker primitives
packages/svelte/src/internal/client/dom/hydration.js exports:
| Function | What it does |
|---|---|
hydrate_template(...) |
Takes a hydratable template fragment and aligns it with the next sibling. |
next() |
Move the cursor to the next sibling. |
reset() |
Reset cursor to the start of the parent. |
set_hydrate_node(n) |
Force the walker to a particular node (used during recovery). |
hydrating flag |
Module-scoped boolean that block runtimes consult before deciding to |
| create new DOM vs. attach to existing. |
The cursor is advanced cooperatively — every block runtime function under dom/blocks/ calls next() after it consumes its comment markers.
Mismatch handling
If the client encounters a tag, attribute, or text that doesn't match the server output:
- For text nodes,
set_text(ininternal/client/render.js) silently rewritesnodeValue. - For attributes,
set_attributeand friends indom/elements/attributes.jscheck the server-rendered value against the desired client value and update only if they differ. This avoids spurious DOM writes while still recovering from genuine mismatches. - For block-level mismatches (e.g., the
ifbranch is different), the runtime emits ahydration_mismatchwarning and re-creates the block.
The HMR helper internal/client/dev/hmr.js reuses the hydration walker logic, since hot-replacing a component requires the same "find existing nodes, attach to them" behaviour.
Hydratable wrapper
hydratable(component) (packages/svelte/src/internal/client/hydratable.js, server companion in internal/server/hydratable.js) marks a component as hydration-ready. The server wrapper opts the renderer into emitting comment markers; the client wrapper sets the appropriate hydratable flag on the root effect.
Async hydration
When the server emits an {@async} block, the corresponding HTML may not yet contain the resolved children — the server can stream them in later. The client walker treats async block markers as "start with the loading branch, listen for stream events". The relevant pieces are in internal/client/dom/blocks/await.js and internal/client/dom/blocks/async.js.
Tests
End-to-end hydration is exercised by packages/svelte/tests/hydration/. Each sample contains:
_config.js— driver options.main.svelte— input._expected.html— server output that must hydrate cleanly.
Running pnpm test hydration walks every sample, calls render() on the server then hydrate() on the result, and asserts the post-hydration DOM matches a target snapshot.
See also
- Server runtime — what the server emits.
- Client runtime — what the client expects.
- Compile pipeline — where the hydration markers are inserted by the compiler.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.