# Prove the cause of a hydration mismatch

> Run hydration-proof with --probe to prove the cause of a hydration mismatch: each page is reloaded with one thing changed, such as the clock or the locale.

Source: https://hydration.jscrate.dev/docs/probes
Last updated: 2026-09-18

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

```bash
npx hydration-proof test --probe
```

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

```ts title="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](#what-do-probes-cost)).

## 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.

| Factor     | What changes                                                                                    | Proves                                                 |
| ---------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| `time`     | The browser clock moves by 3 days, 7 hours, 11 minutes and 13 seconds                           | [Time-dependent value](https://hydration.jscrate.dev/docs/causes/time)              |
| `random`   | A different seed for `Math.random()` and `crypto.getRandomValues()`                             | [Random value](https://hydration.jscrate.dev/docs/causes/random)                    |
| `locale`   | The browser locale becomes the server's (or another one)                                        | [Locale-dependent formatting](https://hydration.jscrate.dev/docs/causes/locale)     |
| `timezone` | The browser timezone becomes the server's (or another one)                                      | [Timezone difference](https://hydration.jscrate.dev/docs/causes/timezone)           |
| `theme`    | Light ↔ dark                                                                                    | [Theme preference](https://hydration.jscrate.dev/docs/causes/theme)                 |
| `viewport` | Desktop ↔ mobile                                                                                | [Screen size or media query](https://hydration.jscrate.dev/docs/causes/media-query) |
| `storage`  | No `localStorage`, `sessionStorage` or `storageState` from the scenario (only when it has them) | [Browser storage](https://hydration.jscrate.dev/docs/causes/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:

```text
    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](https://hydration.jscrate.dev/docs/causes/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](https://hydration.jscrate.dev/docs/issues/hp1001) to [HP1006](https://hydration.jscrate.dev/docs/issues/hp1006),
[HP1012](https://hydration.jscrate.dev/docs/issues/hp1012) and [HP1013](https://hydration.jscrate.dev/docs/issues/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](https://hydration.jscrate.dev/docs/scenarios)).

## Options

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `factors` | `ProbeFactor[]` | all that apply | What to vary. |
| `maxPages` | `number` | 5 | Most pages probed per run. |

## Related

- [How hydration-proof works](https://hydration.jscrate.dev/docs/how-it-works): stages, comparisons and causes
- [All causes of hydration errors](https://hydration.jscrate.dev/docs/causes) and their fixes
- [Environment matrix](https://hydration.jscrate.dev/docs/environment-matrix): find the locale or browser a bug needs
- [Fix date and time hydration errors](https://hydration.jscrate.dev/docs/causes/time)
- [HP1001: text differs between server and client](https://hydration.jscrate.dev/docs/issues/hp1001)
