SSR Hydration and Server-Client Consistency
Pathrule2 Rules • 3 Memories • 1 Skill
Hydration attaches behavior to server-produced HTML under the assumption that the first client render describes the same tree. Time, randomness, environment branches, browser storage, locale, invalid markup, or different data snapshots break that assumption. This pattern constrains deterministic render inputs and stable identity, records browser-only and data-handoff boundaries, and provides a DOM and state comparison workflow across server and client. It complements framework-specific SSR patterns by describing the cross-framework invariant; routing and caching stay with Next.js, Nuxt, SvelteKit, or another renderer.
Suggested path map
Pathrule places each piece on the matching path, so your assistant only sees it where it belongs. This is the scoping you get on import; you can adjust it in your workspace.
Rules
2Render from deterministic, serializable inputs/src/serverhighstrictUse one request snapshot for server HTML and first client render, excluding time, randomness, browser globals, and mutable process state.
| 1 | Hydration fails when the client computes different initial content before it has attached to the server tree. The server snapshot must be transferable and sufficient to reproduce the same visible structure. |
| 2 | |
| 3 | - Capture time, experiment assignment, locale, auth-visible state, and fetched data once per request and serialize only the values the client needs. |
| 4 | - Do not call random generators or process-global counters during component render; derive stable identity from data or a framework-supported deterministic ID mechanism. |
| 5 | - Create request-scoped stores and caches on the server so one request cannot reuse personalized state from another. |
| 6 | - Escape serialized data for its HTML embedding context and reject non-serializable class instances, functions, handles, or cyclic objects at the boundary. |
| 7 | |
| 8 | See /src/client for the adjacent decision or procedure that completes this constraint. |
Introduce browser-only state after hydration/src/clienthighstrictRender a stable server-compatible fallback first, then read storage, viewport, media, DOM, and device APIs in an effect or client-only boundary.
| 1 | The browser has state the server cannot observe, but branching on it during the first render changes text or structure before hydration. That includes theme, connectivity, media queries, extensions, and persisted client preferences. |
| 2 | |
| 3 | - Use a deterministic initial value agreed with the server and update it after mount when browser-only state becomes available. |
| 4 | - For layout-dependent content, preserve a stable container or skeleton so the post-hydration update does not create avoidable layout shift. |
| 5 | - Do not use a broad client-only wrapper to hide server-capable content; isolate only the smallest subtree that genuinely requires browser APIs. |
| 6 | - Treat mismatch suppression as a narrow acknowledgement for inherently unstable text, not a fix for ownership, data, identity, or markup errors. |
| 7 | |
| 8 | See /tests/ssr for the adjacent decision or procedure that completes this constraint. |
Memories
3The hydration payload is a versioned public contract/src/serverSerialize the minimum initial state with schema identity and consume it before starting duplicate client fetches.
| 1 | Server and client bundles can overlap during deployment, and a browser can hydrate HTML produced by an instance on a neighboring version. An unversioned arbitrary object is fragile across that window. |
| 2 | |
| 3 | - Define the payload fields and schema version explicitly and keep additive compatibility through the maximum mixed-deployment and cached-HTML window. |
| 4 | - Embed only data already authorized for the rendered user and route; serialized state is visible page source, not a private server channel. |
| 5 | - Initialize the client cache or store from the payload before automatic fetching begins so the same resource is not requested and replaced immediately. |
| 6 | - Remove payload elements after consumption where appropriate and avoid retaining duplicate large data in both DOM text and multiple client stores. |
| 7 | |
| 8 | See /src/client for the rule or workflow that puts this decision into practice. |
Valid HTML is part of the component contract/src/serverKeep server markup legal and structurally stable because browser parser correction occurs before the framework hydrates it.
| 1 | Browsers repair invalid nesting while parsing the server response. The DOM the framework receives can therefore differ from the string the server emitted even though both server and client component code appear identical. |
| 2 | |
| 3 | - Use semantic elements with legal parent and child relationships and do not nest interactive controls inside other interactive controls. |
| 4 | - Render table, list, form, and paragraph structures according to HTML parsing rules rather than relying on the browser's repair behavior. |
| 5 | - Keep conditional wrappers stable between server and client so keys and sibling positions retain the same meaning. |
| 6 | - Inspect the parsed DOM in a browser when a mismatch survives data checks; comparing server strings alone misses parser-inserted or rearranged nodes. |
| 7 | |
| 8 | See /tests/ssr for the rule or workflow that puts this decision into practice. |
Streaming boundaries have independent failure and reveal behavior/src/clientDesign each streamed boundary with a stable fallback, error path, data owner, and ordering relationship to the hydration payload.
| 1 | Streaming can deliver and hydrate portions of the page at different times. A boundary that reads mutable shared state or depends on a sibling's side effect can behave differently according to reveal order. |
| 2 | |
| 3 | - Give each boundary the data and serialized state it needs without relying on another boundary hydrating first. |
| 4 | - Make fallbacks structurally compatible with the final content where possible and avoid duplicate interactive identifiers across pending and revealed trees. |
| 5 | - Handle server failure before flush, failure after partial flush, and client hydration failure as separate observable outcomes. |
| 6 | - Keep critical metadata and primary content outside unnecessarily delayed boundaries when crawlers and first interaction depend on them. |
| 7 | |
| 8 | See /src/server for the rule or workflow that puts this decision into practice. |
Skills
1investigate-hydration-mismatch/rootCompare server inputs, emitted HTML, parsed DOM, client inputs, and first render to isolate an SSR mismatch.
| 1 | --- |
| 2 | name: investigate-hydration-mismatch |
| 3 | description: Investigate a hydration warning, stale first paint, or server-client DOM replacement. |
| 4 | --- |
| 5 | |
| 6 | # Investigate Hydration Mismatch |
| 7 | |
| 8 | Run this procedure when the affected surface changes, before the result is promoted to production. Record evidence for every step instead of accepting a plausible-looking result. |
| 9 | |
| 10 | 1. Reproduce without extensions and with a fixed clock, locale, timezone, random seed, authentication state, route, and backend snapshot. |
| 11 | 2. Capture the server's request-scoped inputs and hydration payload, then compare them with the values available before the client's first render. |
| 12 | 3. Inspect emitted HTML and the browser-parsed DOM for invalid nesting, inserted elements, changed attributes, or unstable identifiers. |
| 13 | 4. Disable branches selectively for time, randomness, browser APIs, storage, media queries, and immediate refetch to identify the first divergent subtree. |
| 14 | 5. Repair the ownership or serialization boundary, remove unnecessary suppression, and add a server-render plus real-browser hydration regression test for the case. |
| 15 | |
| 16 | ## Exit criteria |
| 17 | |
| 18 | The change is complete only when the expected behavior, failure behavior, and rollback path have all been exercised with representative data. Preserve the evidence with the change so the next operator can repeat the same checks. |
Why this pattern
AI agents often read window or storage during render, generate random IDs, refetch different data before hydration, or suppress mismatch warnings without repairing the divergent state.
Built for Frontend teams operating server-rendered React, Vue, Svelte, or other hydrated web applications.
Keeps your assistant from:
- Attaching state and listeners to the wrong server-rendered node
- Flashing different authenticated or personalized content on first paint
- Duplicating data requests before the serialized snapshot hydrates
- Hiding a genuine mismatch with a suppression flag
- License
- Apache-2.0
- Version
- 1.0.0
- Updated
- 2026-08-25