Hydration Proof

Search documentation

Find a page or section

Six snapshots of every page, compared in a real browser.

Here is how hydration-proof detects mismatches: it opens each page in a browser and records six points between the server response and the settled page. It compares those points to show when a difference first appeared. A test-only runtime connects to React; nothing is added to your app.

Connecting to React

hydration-proof uses Playwright to load the page. Before your scripts run, it adds a small runtime to the test browser.

React uses __REACT_DEVTOOLS_GLOBAL_HOOK__ to notify tools about renders and commits. The runtime provides that hook or wraps an existing one, so React DevTools and Fast Refresh can still work.

The hook also lets hydration-proof record recoverable errors and their component stacks, even when the app has its own error handler.

How hydration-proof detects mismatches

The six stages

StageWhat it shows
1. Server HTMLThe document response received on this page load
2. Parsed HTMLHow the browser parses it with scripts blocked
3. Pre-hydration DOMThe DOM just before React's hydration changes
4. Hydrated DOMThe DOM and React props during the hydration commit
5. After effectsThe first quiet point after effects run
6. StableThe page after it stays quiet for ready.quietMs

Each node has an identity in the comparison, so a reused node can be told from a replacement. The ready options control when the page counts as settled.

The four comparisons

ComparedWhat it finds
1 → 2HTML the browser repaired, such as invalid nesting
2 → 3Changes made by scripts or extensions before hydration
3 → 4Content React replaced during hydration
3 ↔ React propsText, styles, and attributes that differ from React's render

If several comparisons find the same problem, the report shows one finding. It includes React's error when available and gives the finding an issue code. The stage tells you whether the difference began in server markup, before hydration, or during React's render.

Explaining a finding

The report tries to answer three questions for each finding:

  • Where did it happen? It uses React's development location or available source maps to point to the component and file. Production locations depend on browser source maps.
  • Why did it happen? It uses the changed values, source code, scenario, and stage to suggest a cause. See the cause guides.
  • What can fix it? It shows advice for the cause and the issue code.

If the exact source location is unavailable, the report says so.

Proving a cause with probes

The initial cause is a suggestion. With --probe, hydration-proof reloads the page while changing one factor at a time: clock, random seed, locale, timezone, theme, viewport, or storage.

If changing one factor changes or removes the finding, the report identifies that factor. If an identical reload changes the output, look at server data. Probes change the test browser, not your server.

npx hydration-proof test --probe

Probes lists every factor and what it costs.

Environments and flaky findings

The matrix option checks combinations of environment values, such as locale, timezone, theme, viewport, browser, and feature flags. Pairwise selection reduces the number of runs while still covering pairs of values. The report shows which conditions are associated with a finding. See the environment matrix.

Use --repeat to catch findings that appear only sometimes. The report marks them as flaky.

Streaming, Suspense and navigation

Streaming pages hydrate in stages. hydration-proof checks the root and each Suspense boundary as it hydrates. It does not mistake content arriving from the stream for a mismatch. If a boundary fails, the report includes React's error.

The whole document is checked too:

  • <head> values that hydration adds or changes (HP1014). React 19 adds a new <title> or <meta> instead of fixing the server one.
  • duplicate ids (HP3003)
  • useId collisions between several React roots on one page (HP3004)

With checks.navigation, hydration-proof compares client-side navigation with a direct page load. With checks.interactions, it checks what happens when someone types, clicks, or scrolls before hydration finishes. See interactions and navigation.

Development and production builds

--mode both checks the development server and a production build.

  • Development builds give component names and exact source lines.
  • Production builds show what users get. React 19 in production does not report attribute mismatches at all.

The report marks findings that appear in only one mode.

npx hydration-proof test --mode both

What it does not change

The check does not edit your app or add code to its production bundle. Its runtime exists only in the test browser.