# Compatibility and stability

> hydration-proof compatibility: supported Node.js, React, Next.js, framework, browser and package manager versions, what is stable, and how deprecation works.

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

hydration-proof compatibility covers two things: the versions of Node.js,
React, frameworks, browsers and package managers it is tested with, and the
parts of the tool you can build on. Everything listed as stable follows
[semver](https://semver.org/): it changes only in a major release, with a
migration path.

## The hydration-proof compatibility table

|                      | Supported                                                                                                                                                                                   |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Node.js**          | 22.18 and later, the first version that imports `.ts` directly. Tested on 22.18, 24 and 26, on Linux, macOS and Windows                                                                     |
| **React**            | 18.3 and 19.x, in development **and** production builds. React 18.0–18.2 are not tested; they hydrate differently enough that findings may be less precise                                  |
| **Next.js**          | 15.5 and 16.x, App Router and Pages Router, webpack and Turbopack. The App Router always uses Next.js's own bundled React, so the installed React version only matters for the Pages Router |
| **React Router**     | Framework mode (`@react-router/dev`); the fixtures run version 8.4                                                                                                                          |
| **Other frameworks** | Remix v2, Astro with React islands, Vite SSR, and custom Node.js servers                                                                                                                    |
| **Browsers**         | Chromium, Firefox and WebKit through Playwright. CPU slowdown and native network throttling are Chromium only                                                                               |
| **Playwright**       | `playwright-core` 1.63 and later. Your own copy is used when it is new enough, so browsers are never downloaded twice                                                                       |
| **Package managers** | npm, pnpm, Yarn Classic, Yarn Berry (including Plug'n'Play) and Bun                                                                                                                         |
| **ESLint plugin**    | ESLint 9 and 10, flat config only                                                                                                                                                           |

Each package manager is tested by installing the packed tarball into a fresh
app and running the CLI against it. A version leaving this table is a major
release; adding one is not.

[Adapters](https://hydration.jscrate.dev/docs/adapters) explains how each framework is built, started and
read.

## How the table is checked

Two workflows in the repository back the table with evidence:

- **`compat.yml`** runs the whole fixture suite again against other Next.js
  versions (16.3, 16.2 and 15.5), and against React 18.3 and 19.0 to 19.3 for
  the Pages Router and custom SSR.
- **`real-world.yml`** runs the packed CLI against pinned open-source Next.js
  apps that nobody on the project wrote: Next.js's own `blog-starter`,
  `with-styled-components` and `app-dir-i18n-routing` examples, pinned to a
  release tag. It runs without a config file, the way a first run on a real app
  does, and with `--fail-on warning`: the apps are expected to be clean, so any
  finding above info is a false positive to investigate.

A tool that only works on its own fixtures is not a tool, which is why the
second workflow matters most.

## What is stable

| Surface                    | Stable part                                                                                                                                                                |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **CLI**                    | The commands, their flags and the exit codes. New flags are added; existing ones keep their meaning                                                                        |
| **Config**                 | Every documented option and `configVersion: 1`. Removing or renaming one needs a major release, and `hydration-proof migrate` keeps working for at least one major version |
| **Report**                 | `report.json` with `schemaVersion: 1`, described by `schema/report.json`. Fields are added, never removed or repurposed                                                    |
| **Issue codes**            | An `HPxxxx` code keeps its meaning forever and is never reused for something else                                                                                          |
| **Fingerprints**           | Stable for a given `fingerprintVersion`, so baselines and ignore rules keep matching                                                                                       |
| **Node API**               | `run()`, `defineConfig`, `definePlugin`, `defineAdapter`, `mergeReports`, `ISSUES` and the types they use                                                                  |
| **Plugin and adapter API** | The fields documented for [plugins](https://hydration.jscrate.dev/docs/plugins) and [adapters](https://hydration.jscrate.dev/docs/adapters)                                                                                          |
| **Reporter output**        | The JUnit, SARIF and GitLab shapes, as their own schemas define them                                                                                                       |

The [issue codes](https://hydration.jscrate.dev/docs/issues) and [CLI](https://hydration.jscrate.dev/docs/cli) pages list the stable
values.

## What is not stable

These are internals. They change in minor releases, and nothing outside the
package should depend on them:

- everything the public exports do not re-export, including the capture
  stages, the mutation log, the rewind and the diff
- the browser runtime and its protocol, which is rebuilt and inlined on every
  release (both sides always ship together)
- the HTML report's markup and its embedded data
- the exact wording of messages, the set of `evidence` entries, and confidence
  scores
- **which** cause a finding is given, and whether it has a cause at all.
  Diagnosis improves over releases, so treat a cause as an explanation for a
  human, not as something to assert on

`severity` is in between: a code's severity can be lowered or raised in a
minor release when experience shows it was wrong. Pin the behavior you care
about with `ci.failOn` and `ci.budget` rather than relying on the defaults.
[Baselines and budgets](https://hydration.jscrate.dev/docs/baselines) shows how.

## Fingerprints and baselines

A fingerprint identifies a finding across runs, so a baseline recorded today
still matches tomorrow. It is built from the issue code, the route pattern, the
scenario and the element's position. It never uses values that change per
request, or anything redaction rewrites: fingerprints are computed first.

If a fix has to change how fingerprints are computed, `fingerprintVersion` in
the report is raised, the release notes say so, and `hydration-proof baseline`
rewrites your baseline. Old baselines keep working until you record them again:
entries with an older `fingerprintVersion` are matched by code, route and
selector instead.

## How deprecation works

When something stable has to go:

1. It keeps working, and using it prints a note that names the replacement.
2. `hydration-proof migrate` rewrites it where a rewrite is possible.
3. It is removed in the next major release at the earliest.

Config options are the common case, and `migrate` covers them. Nothing is ever
removed in a patch release.

## Upgrading

List what the new version renamed or replaced, then apply the safe renames:

```bash
npx hydration-proof migrate
npx hydration-proof migrate --write
```

`--write` keeps the old file as `<file>.backup`. Changes that need a judgment
call are listed for you to make by hand.

Between minor versions, expect new findings: better detection is the point. If
a release reports something you have decided to live with,
[an ignore rule](https://hydration.jscrate.dev/docs/ignoring) with a reason and an expiry date is the honest
way to park it. `ci.newIssuesOnly` (`--new-only`) lets you adopt a release
without a red pipeline on day one.

## Related

- [Release notes](https://hydration.jscrate.dev/docs/releases)
- [Node API](https://hydration.jscrate.dev/docs/node-api): the stable programmatic surface
- [Reports](https://hydration.jscrate.dev/docs/reports): the `report.json` schema and other formats
- [Troubleshooting](https://hydration.jscrate.dev/docs/troubleshooting)
- [Quick start](https://hydration.jscrate.dev/docs/quick-start)
