A hydration error baseline and budget help when your app already has known problems. Save today's findings in a baseline to fail CI only on new ones. Use a budget to limit findings by severity, route or issue code. You can also assign owners and track whether the counts improve.
Record a baseline
Record the current findings:
npx hydration-proof baseline # records the current findingsCommit the baseline so CI can compare future runs with it:
git add .hydration-proof/baseline.jsonThen fail CI only on findings that are not in it:
npx hydration-proof test --new-onlyYou can set ci.newIssuesOnly: true in the config instead. Known findings
remain visible with an "in baseline" label; new ones are marked "new" and
can fail the run. The terminal summary counts both.
baseline takes the same options as test. The file lives at
.hydration-proof/baseline.json; change the path with ci.baseline.
How baseline entries match
An entry matches by its fingerprint: issue code, route pattern, element and attribute. Editing another part of the page does not turn it into a new finding, but a different problem on the same page is still new.
Each entry records the route, the issue, the likely cause, the scenarios it was seen in, and the dates it was first and last seen:
{
"$schema": "../node_modules/hydration-proof/schema/baseline.json",
"version": 1,
"tool": "hydration-proof",
"updatedAt": "2026-09-18T09:12:44.120Z",
"fingerprintVersion": 1,
"entries": [
{
"fingerprint": "3f9c2a7d41e0b865",
"code": "HP1004",
"title": "Class name differs between server and client",
"route": "/settings",
"scenarios": ["default"],
"firstSeen": "2026-09-18",
"lastSeen": "2026-09-18",
"selector": "#theme",
"attribute": "class",
"cause": "theme",
"expires": "2026-12-31",
"reason": "The theme toggle rewrite is tracked in ACME-512."
}
]
}Add a reason and an expiry date
Add a reason and an expires date (YYYY-MM-DD) to an entry when you accept
it temporarily. Reports show the reason. After the date, the finding can fail
CI again, so an exception does not last forever.
Update the baseline
After fixing problems, record again:
npx hydration-proof baseline
# or, as part of a normal run:
npx hydration-proof test --update-baselineFixed entries are removed. Hand-written fields and firstSeen dates are kept
for entries that remain.
If a release has to change how fingerprints are computed, the report's
fingerprintVersion goes up. Until you record again, entries with an older
version are matched by code, route and selector instead, so the pipeline does
not turn red overnight.
Budgets
A budget sets the number of findings CI will allow:
import { defineConfig } from "hydration-proof";
export default defineConfig({
ci: {
budget: {
error: 3,
warning: 20,
routes: { "/checkout/**": { error: 0, warning: 0 } },
codes: { HP1004: 2 },
},
},
});error,warning,info: the most findings of that severity in the run. A budget for a severity replaces the "any finding fails" rule for that severity.routes: limits per severity for the routes matching each glob.codes: limits per issue code, such as{ HP1004: 3 }.
When a limit is exceeded, the run fails and names the limit—for example, "4 errors exceed the budget of 3." Lower the limits as you fix problems.
failOn and maxWarnings
ci.failOn is the lowest severity that fails the run: 'error' (the default),
'warning', 'info' or 'never'. --fail-on sets it for one run.
ci.maxWarnings fails the run when there are more warnings than this, even
when warnings do not fail it on their own.
Set these explicitly if your CI result must stay consistent: an issue code's default severity may change in a minor release (see compatibility).
Combine a hydration error baseline and budget
For an app with known findings, combine the baseline with limits for the remaining issues. In this example, checkout must have no errors or warnings:
import { defineConfig } from "hydration-proof";
export default defineConfig({
ci: {
failOn: "error",
// Only findings that are not in the baseline fail.
newIssuesOnly: true,
baseline: ".hydration-proof/baseline.json",
// The ratchet: the numbers may only go down.
budget: {
error: 0,
warning: 12,
routes: { "/checkout/**": { error: 0, warning: 0 } },
codes: { HP1004: 3 },
},
history: true,
},
// Who gets pinged, from CODEOWNERS unless the route says otherwise.
owners: {
routes: { "/checkout/**": ["@acme/payments"] },
codeowners: true,
},
});Detect hydration errors in CI has the workflows to run it in.
Owners
Findings show who owns them. Route owners come from owners.routes; for other
findings, the CODEOWNERS entry of the finding's source file is used.
hydration-proof looks for .github/CODEOWNERS, CODEOWNERS,
docs/CODEOWNERS and .gitlab/CODEOWNERS, or the path you give in
owners.codeowners. Set it to false to turn the lookup off.
The terminal prints the owners under each finding, report.json has them in
owners, and the HTML report can be filtered by owner.
Trends
ci.history: true appends one line per run to .hydration-proof/history.ndjson
(or to the path you give). Each line holds the date, the commit and branch, the
duration, the number of pages and failed pages, and the findings by severity
and by issue code. Ignored findings are not counted.
The HTML report shows the errors and warnings of the last 30 runs. Keep the
file between CI runs, for example with actions/cache or by committing it on
the main branch; otherwise the trend starts over every run.
Options
| Option | Type | Default | Description |
|---|---|---|---|
| failOn | Severity | "never" | "error" | Lowest severity that makes the run fail. |
| maxWarnings | number | — | Fail when more warnings than this are found. |
| baseline | string | .hydration-proof/baseline.json | Baseline file of accepted issues. |
| newIssuesOnly | boolean | — | Only fail on issues that are not in the baseline. |
| budget | BudgetConfig | — | Hydration error budget: how many findings of a severity (in total, per route glob or per code) are allowed before the run fails. |
| history | boolean | string | — | Append a line per run to a history file for trends: true for .hydration-proof/history.ndjson, or a path. |
| Option | Type | Default | Description |
|---|---|---|---|
| routes | Record<string, BudgetLimits> | — | Limits for the findings of routes matching each glob. |
| codes | Record<string, number> | — | Limits per issue code, e.g. { HP1004: 3 }. |
| error | number | — | Inherited from BudgetLimits. |
| warning | number | — | Inherited from BudgetLimits. |
| info | number | — | Inherited from BudgetLimits. |
| Option | Type | Default | Description |
|---|---|---|---|
| routes | Record<string, string | string[]> | — | Route glob → owners, e.g. { '/checkout/**': ['@acme/payments'] }. |
| codeowners | boolean | string | — | Owners of source files from CODEOWNERS: true (default) looks in .github/, docs/ and the repository root; or a path. |
Related
- Detect hydration errors in CI: workflows, sharding and
--changed - Ignore known hydration mismatches one at a time
- Reports: the
new,baselineandownersfields - The
cioption in the configuration reference - What is stable between releases