Open-Source Wikis

/

Svelte

/

Systems

/

Hydration

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 (in internal/client/render.js) silently rewrites nodeValue.
  • For attributes, set_attribute and friends in dom/elements/attributes.js check 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 if branch is different), the runtime emits a hydration_mismatch warning 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

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Hydration – Svelte wiki | Factory