Hydration Proof

Search documentation

Find a page or section

Prove the cause of a hydration mismatch

Reload the page with one thing changed, and see which change moves the value.

Probes prove the cause of a hydration mismatch instead of guessing it. With --probe, hydration-proof reloads each page that has a value mismatch with the browser clock and random values fixed, then once per factor with exactly one thing changed. If the client value changes, or the finding disappears, when only that factor changes, the factor is the cause.

Turn probes on

npx hydration-proof test --probe

Or in the config, with probes: true, or with options:

hydration-proof.config.ts
import { defineConfig } from "hydration-proof";
 
export default defineConfig({
  probes: { factors: ["time", "random"], maxPages: 5 },
});

Probes are off by default because they cost extra page loads (see below).

How to prove the cause of a hydration mismatch

Every finding already has a likely cause: an informed guess, with a confidence, made from the values that differ, the code around the source line, the scenario and the stage where the difference started. A probe turns the guess into a test:

  1. The page is loaded with the browser clock and Math.random() fixed.
  2. It is loaded again, identically. This is the control.
  3. It is loaded once more for each factor, with only that factor changed.
  4. hydration-proof compares the finding across the loads.
FactorWhat changesProves
timeThe browser clock moves by 3 days, 7 hours, 11 minutes and 13 secondsTime-dependent value
randomA different seed for Math.random() and crypto.getRandomValues()Random value
localeThe browser locale becomes the server's (or another one)Locale-dependent formatting
timezoneThe browser timezone becomes the server's (or another one)Timezone difference
themeLight ↔ darkTheme preference
viewportDesktop ↔ mobileScreen size or media query
storageNo localStorage, sessionStorage or storageState from the scenario (only when it has them)Browser storage

The clock moves by an odd amount so that every field of a date changes at once: the day, the hour, the minute and the second.

Proven or likely?

A finding whose client value changes, or that disappears, when only one factor changes has that cause proven. The terminal shows proven where it otherwise shows a percentage, and the run ends with a summary of what the probes found:

    HP1001 Text differs between server and client  (timezone difference, proven)
      #last-login  in LastLogin
      server: "Signed in at 5:00 AM"
      client: "Signed in at 10:00 AM"
 
  Probes
    HP1001 /dashboard #last-login  proven: timezone difference

The finding's evidence says what changed, for example that the client rendered one value instead of another when only the browser timezone changed. Its list of fixes starts with the fixes for the proven cause.

When several factors change the finding, the most specific one names the cause, and the evidence lists the others. Factors that changed nothing are listed too, and a likely cause that a probe ruled out loses confidence.

What if an identical reload renders differently?

Then the value does not come from the browser. The clock and random values were fixed, so it comes from the server or an API that answers differently each time. The finding's cause becomes server and client used different data, and the other factors are marked inconclusive, since a page that changes on its own cannot prove anything else.

What do probes cost?

Only value mismatches are probed: text, attribute, style, class and dangerouslySetInnerHTML differences, plus form state (HP1001 to HP1006, HP1012 and HP1013). Ignored, suppressed and info findings are skipped.

  • Each probed page costs up to 9 extra page loads: the fixed load, the control, and one per factor.
  • maxPages (default 5) caps the pages probed per run. Pages with the most errors go first, and a note says how many were left out.
  • The run ends with a note of how many extra loads the probes took.

Narrow factors when you only suspect one or two causes.

Probes never hide a finding

The server is never changed. Fixing the browser clock only makes the loads comparable with each other; the server keeps its real clock, so a time-dependent value is still found. The same goes for a scenario's clock and randomSeed options: they make client values repeatable between runs, and hide nothing (see scenarios).

Options

OptionTypeDefaultDescription
factorsProbeFactor[]all that applyWhat to vary.
maxPagesnumber5Most pages probed per run.