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
| Stage | What it shows |
|---|---|
| 1. Server HTML | The document response received on this page load |
| 2. Parsed HTML | How the browser parses it with scripts blocked |
| 3. Pre-hydration DOM | The DOM just before React's hydration changes |
| 4. Hydrated DOM | The DOM and React props during the hydration commit |
| 5. After effects | The first quiet point after effects run |
| 6. Stable | The 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
| Compared | What it finds |
|---|---|
| 1 → 2 | HTML the browser repaired, such as invalid nesting |
| 2 → 3 | Changes made by scripts or extensions before hydration |
| 3 → 4 | Content React replaced during hydration |
| 3 ↔ React props | Text, 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 --probeProbes 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 bothWhat 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.
Related
- Quick start: install and run your first test
- Prove the cause of a mismatch with probes
- Every issue code, from HP1001 to HP9010
- Common causes of hydration errors and their fixes
- Reports: the HTML report, JSON, JUnit and SARIF