vuejs/core
Hydration
Hydration is the process of binding existing server-rendered DOM to a client-side component tree without throwing the DOM away and re-creating it. The implementation is one of the most subtle parts of Vue's runtime: packages/runtime-core/src/hydration.ts is ~30KB and almost entirely defensive code that handles every shape of mismatch.
For the SSR side that produces the markup, see server-side rendering.
Entry points
graph TD
A["createSSRApp(...)"]
B["createHydrationRenderer(rendererOptions)"]
C["app.mount(container)"]
D["hydrate(rootVNode, container)"]
E["hydrateNode(node, vnode, parent, …)"]
A --> B --> C --> D --> EcreateSSRApp is in packages/runtime-dom/src/index.ts. It calls ensureHydrationRenderer() (which lazily creates a hydration-capable renderer via createHydrationRenderer from runtime-core/src/renderer.ts). On mount, the hydration renderer's hydrate function (defined in hydration.ts) walks the existing DOM and the new vnode tree in lockstep.
hydrateNode
The core function in hydration.ts is hydrateNode(node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized). It:
- Inspects the existing DOM node's
nodeTypeandnodeName. - Branches on the vnode's
ShapeFlags:ELEMENT→ match tag, hydrate attributes (events attached, no DOM changes), recurse into children withhydrateChildren.TEXT/COMMENT→ match content; warn on mismatch.STATIC→ consume one or more sibling nodes equal to the static vnode's count.FRAGMENT→ consume between fragment markers (<!---->...<!---->).STATEFUL_COMPONENT/FUNCTIONAL_COMPONENT→ set up the component, recurse into its sub-tree.TELEPORT→ use the specialTeleport.hydratethat knows how to find content at the target selector.SUSPENSE→ set up aSuspenseBoundaryand decide whether to hydrate the default slot or the fallback based on thedata-server-renderedpayload.
- Returns the next sibling for the caller to continue from.
When a mismatch is detected (text doesn't match, tag doesn't match, attribute set differs):
- A dev warning is emitted via
logMismatchError. The warning includes the vnode location (file/line in compiled SFCs). - The server-rendered subtree is removed and replaced by a freshly mounted client vnode tree (full client mount fallback).
Patch flags during hydration
The optimization story carries through to hydration: the same PatchFlags the compiler emitted are honored. With PatchFlags.HYDRATE_EVENTS, the renderer attaches event listeners but skips re-running prop diffing. With STABLE_FRAGMENT, it knows children won't reorder. With BAIL, it disables optimizations and treats the subtree as if it might have any kind of change — used when the runtime can't trust the patch flag (e.g., manual cloneVNode).
Lazy hydration strategies
packages/runtime-core/src/hydrationStrategies.ts exports four strategies for defineAsyncComponent({ hydrate: hydrateOnXxx() }):
hydrateOnIdle(timeout?)— wait untilrequestIdleCallbackfires.hydrateOnVisible(opts?)— wait until anIntersectionObserverreports the root element visible.hydrateOnMediaQuery(query)— hydrate only whenmatchMedia(query)matches.hydrateOnInteraction(events)— hydrate on the first matching event (click,pointerover, etc.).
A lazy-hydrated component renders a placeholder (the SSR markup is left untouched) until the strategy fires, then the real component mounts and binds.
This is invaluable for "islands of interactivity" patterns: a 100-component page can be statically rendered, with only the components the user actually interacts with becoming reactive.
Teleport hydration
Teleports are tricky because their target is by CSS selector, not by DOM tree position. compiler-ssr renders the teleport's children into the SSR string at the teleport's logical position wrapped in <!--teleport start--> / <!--teleport end--> markers; the server caller is expected to splice those into the right place in the final HTML by reading ssrContext._teleports. Hydration on the client uses Teleport.hydrate (packages/runtime-core/src/components/Teleport.ts) which:
- Walks from the teleport's starting marker to its end marker.
- Locates the actual DOM target via
targetselector / element. - Verifies the children match what the vnode expects.
If the markers are missing (server-side teleports weren't merged), hydration falls back to a client-side teleport mount.
Suspense hydration
Suspense.hydrate decides whether the SSR rendered the default slot (success) or the fallback slot (timeout). It uses a marker comment whose content indicates the choice. If the SSR rendered fallback, the client repeats the wait, then replaces fallback with the resolved default slot once dependencies finish.
Mismatch reporting
Two flags control mismatch behavior:
__DEV__— full mismatch warnings, including a structured payload of expected vs actual.__VUE_PROD_HYDRATION_MISMATCH_DETAILS__— keeps detailed payloads in production at the cost of bundle size. Defaultfalse.
The logMismatchError function in hydration.ts is the single place mismatch payloads are formatted.
Files to know
packages/runtime-core/src/hydration.tspackages/runtime-core/src/hydrationStrategies.tspackages/runtime-core/src/renderer.ts(search forcreateHydrationRenderer)packages/runtime-core/src/components/Teleport.ts(Teleport.hydrate)packages/runtime-core/src/components/Suspense.ts(Suspense.hydrate)packages/runtime-dom/src/index.ts(createSSRApp,hydrateexports)packages/runtime-dom/src/directives/vModel.ts(getSSRProps,initVModelForSSR)packages/runtime-dom/src/directives/vShow.ts(initVShowForSSR)
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.