Hydration Proof

Search documentation

Find a page or section

From install to your first report in three commands.

hydration-proof finds React hydration errors by loading every route of your server-rendered app in a real browser and comparing the server HTML with what React renders on the client. Install it, run npx hydration-proof test, and it tells you which element differs, both values, the likely cause and the fix.

Install hydration-proof

Add it as a dev dependency, then download the browser once:

npm install -D hydration-proof
npx hydration-proof install  # downloads Chromium

It needs Node.js 22.18 or newer, and works with npm, pnpm, Yarn (including Plug'n'Play) and Bun. There are no install scripts: browsers are only downloaded by the install command, into Playwright's shared cache, so a project that already uses the same Playwright version does not download them again.

To catch the same mistakes in your editor, add the ESLint plugin as well:

npm install -D eslint-plugin-hydration-proof

Run your first test

npx hydration-proof init  # creates hydration-proof.config.ts
npx hydration-proof test

For Next.js, React Router, Remix and Astro apps that is all. hydration-proof:

  1. discovers your routes from app/, pages/ and the build output,
  2. builds the app if there is no build yet, and starts it on a free port,
  3. loads every route and compares the server HTML with the hydrated DOM,
  4. exits with code 1 if it found a problem.

Vite SSR and custom Node servers are detected too. Any other setup works with --url (see adapters).

Read the report

Each finding in the terminal shows the issue code, the element, the value on each side, the source line and a fix:

Hydration Proof — 20 pages on http://localhost:3000
 
  ✓ /pricing 684ms
  ✖ /dashboard 1.1s  1 error
    HP1001 Text differs between server and client  (timezone difference, 95%)
      #last-login  in LastLogin
      server: "Signed in at 5:00 AM"
      client: "Signed in at 10:00 AM"
      app/dashboard/LastLogin.tsx:14:10
      → Pass an explicit timeZone to the formatter (the same on both sides), or format the date after mount.
 
  Report: .hydration-proof/report/report.html
  • HP1001 is a stable issue code with its own page.
  • timezone difference, 95% is the likely cause and how sure hydration-proof is.

Open .hydration-proof/report/report.html for everything at once: filters, the server and client values side by side, the code, screenshots with the element outlined, and a timeline of the page. Reports covers every output format.

Test an app that is already running

npx hydration-proof test --url http://localhost:3000 --route / --route /pricing

With --url, nothing is built or started, and only the routes you name are tested. Add --sitemap to test the pages in your sitemap, or --crawl to follow links from the tested pages.

Test development and production builds

Development builds give exact source lines. Production builds show what your users get, including attribute mismatches React 19 never reports in production. Test both in one run:

npx hydration-proof test --mode both

Add it to CI

.github/workflows/hydration.yml
- run: npm ci
- run: npx hydration-proof install --with-deps
- run: npx hydration-proof test

Or let npx hydration-proof init --ci github write the workflow for you. Detect hydration errors in CI covers GitHub Actions, GitLab and CircleCI, sharding, baselines and budgets.

Configure it

The config file is optional for most apps. When you need it, it is TypeScript with completion:

hydration-proof.config.ts
import { defineConfig } from "hydration-proof";
 
export default defineConfig({
  routes: {
    // Example values for dynamic routes
    dynamic: { "/products/[id]": ["1", "42"] },
    exclude: ["/api/**"],
  },
  // The environments your users have
  scenarios: [
    { name: "default" },
    { name: "dark-mobile", colorScheme: "dark", viewport: "mobile" },
    { name: "karachi", locale: "ur-PK", timezoneId: "Asia/Karachi" },
  ],
});

See configuration for every option, and scenarios for signed-in pages.