# Configuration

> Every hydration-proof config option: the file, defineConfig, and the server, routes, scenarios, matrix, probes, ready, checks, ignore, CI and hooks settings.

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

The hydration-proof config is optional: the framework, the build and start
commands and the routes are detected. When you need to change something, write
a TypeScript file that exports `defineConfig({ ... })`. Every option has a
default, and command-line flags override the file.

## The hydration-proof config file

hydration-proof reads the first of these files it finds in the directory it
runs in: `hydration-proof.config.{ts,mts,js,mjs,cjs,json}`.

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  routes: {
    dynamic: { "/products/[id]": ["1", "42"] },
  },
});
```

- **TypeScript is loaded by Node.js itself**, with no extra tooling (it needs
  Node.js 22.18 or newer). Use plain types only: no `enum`, no `namespace` and
  no parameter properties. Relative imports need their file extension
  (`./routes.ts`).
- **`defineConfig`** returns the object unchanged. It is there for type
  checking and completion.
- **Unknown options are errors**, with a suggestion when the name looks like a
  typo ("is not a known option. Did you mean ...?").
- **`configVersion`** is optional and currently always `1`. Setting it lets a
  future version that changes the format migrate the file instead of guessing.
- **An older config** is updated with `hydration-proof migrate` (see the
  [CLI](https://hydration.jscrate.dev/docs/cli#migrate)).

For a JSON config, point `$schema` at the schema in the package to get
completion in your editor:

```json title="hydration-proof.config.json"
{
  "$schema": "./node_modules/hydration-proof/schema/config.json",
  "routes": { "exclude": ["/api/**", "/admin/**"] }
}
```

The same schema is published at `https://hydration.jscrate.dev/schema/config.json`.

## `server`

How the app is built and started, or the URL of one that is already running.
The adapter supplies the commands, so most apps leave this out. `{port}` in a
command is replaced with the chosen port, which is also passed as `PORT`. The
app, and everything it spawned, is stopped when the run ends.

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  server: {
    build: "pnpm build",
    command: "pnpm start --port {port}",
    // Build only when there is no build output yet; "always" in CI
    buildWhen: "if-missing",
    env: { NEXT_PUBLIC_API_URL: "http://localhost:4000" },
    timeout: 180_000,
  },
});
```

To test an app you started yourself, set `url` (or pass `--url`): nothing is
built or started. `mode: "both"` tests the production build and the
development server (`devCommand`) in one run.

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `command` | `string` | the adapter's start command | Command that starts the app. `{port}` is replaced with the chosen port. |
| `build` | `string \| false` | the adapter's build command | Command that builds the app for production. `false` never builds. |
| `buildWhen` | `"always" \| "if-missing" \| "never"` | `"if-missing"` | Build before testing: always, only when no build output exists, or never. |
| `url` | `string` | — | URL of an app that is already running. When set, nothing is started. |
| `port` | `number` | a free port | Port for the started app. |
| `cwd` | `string` | the config file's directory | Working directory for the commands. |
| `env` | `Record<string, string>` | — | Extra environment variables for the app. |
| `timeout` | `number` | 120000 | Milliseconds to wait for the app to answer. |
| `reuseExisting` | `boolean` | `true` outside CI | Use an app already listening on the URL instead of starting one. |
| `mode` | `BuildMode \| "both"` | `"production"` | Test the production build, the dev server, or both (and compare). |
| `devCommand` | `string` | the adapter's dev command | Development server command when both modes are tested. |

## `routes`

Which pages are tested. Without `paths`, routes are discovered from the
framework and the build output. Dynamic routes need example values, or they are
skipped. [Routes](https://hydration.jscrate.dev/docs/routes) explains every source, the glob syntax and the
route cache.

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  routes: {
    discover: true,
    // Routes discovery cannot see: redirects, rewrites, anything behind a flag
    paths: ["/pricing?plan=team"],
    // One real value per dynamic segment, or the route is skipped
    dynamic: {
      "/blog/[slug]": ["hello-world"],
      "/products/[id]": ["1", "42"],
    },
    exclude: ["/api/**", "/admin/**"],
    sitemap: true,
  },
});
```

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `paths` | `(string \| RouteEntry)[]` | `["/"]` when discovery is off | Routes to test. Strings are paths. |
| `dynamic` | `Record<string, string[]>` | — | Example values for dynamic segments, e.g. `{ "/products/[id]": ["1", "42"] }`. |
| `include` | `string[]` | — | Glob patterns (`*`, `**`) a route must match. |
| `exclude` | `string[]` | — | Glob patterns of routes to skip. |
| `discover` | `boolean` | `true` when `paths` is empty | Find routes from the framework (Next.js app/ and pages/, plus build manifests). |
| `query` | `Record<string, string[]>` | — | Query-string variants per route pattern, e.g. `{ "/search": ["?q=shoes", "?q=&page=2"] }`. |
| `sitemap` | `boolean \| string` | — | Read routes from the sitemap: `true` for /sitemap.xml (and robots.txt), or a sitemap URL/path. |
| `crawl` | `boolean \| CrawlConfig` | — | Follow same-origin links found on tested pages. |
| `notFound` | `boolean` | `true` for Next.js | Also test a URL that does not exist, to check the not-found page hydrates. |
| `manifestExamples` | `number` | 3 | Most example values taken per dynamic route from build manifests. |

## `scenarios`

The browser environments every route is tested in. By default there is one
scenario, `default`, that uses this machine's locale and timezone and a light
color scheme, so a server started on the same machine renders with the same
values. Scenarios also carry cookies, storage and a `login` function for
signed-in pages; see [scenarios](https://hydration.jscrate.dev/docs/scenarios).

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  scenarios: [
    { name: "default" },
    { name: "dark-mobile", colorScheme: "dark", viewport: "mobile" },
    { name: "karachi", locale: "ur-PK", timezoneId: "Asia/Karachi" },
  ],
});
```

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `name` (required) | `string` | — | Unique name, shown in reports and used by `--scenario`. |
| `locale` | `string` | this machine's locale | BCP 47 locale for the browser (also sets Accept-Language). |
| `timezoneId` | `string` | this machine's timezone | IANA timezone for the browser, e.g. `Asia/Karachi`. |
| `colorScheme` | `ColorScheme` | `"light"` | Emulated `prefers-color-scheme`: `"light"`, `"dark"` or `"no-preference"`. |
| `reducedMotion` | `"reduce" \| "no-preference"` | — | Emulated `prefers-reduced-motion`. |
| `viewport` | `ViewportOption` | — | Viewport size as `{ width, height }`, or a preset: `"mobile"` (390×844, touch), `"tablet"` (820×1180) or `"desktop"` (1280×800). |
| `userAgent` | `string` | — | User agent string for the browser context. |
| `storageState` | `string` | — | Playwright storage state file (cookies and localStorage), e.g. a logged-in user. |
| `cookies` | `CookieConfig[]` | — | Cookies set before every page load, e.g. a session cookie for a signed-in user. |
| `headers` | `Record<string, string>` | — | Extra request headers. |
| `localStorage` | `Record<string, string>` | — | `localStorage` entries set before page scripts run. |
| `sessionStorage` | `Record<string, string>` | — | `sessionStorage` entries set before page scripts run. |
| `initScripts` | `string[]` | — | Scripts run before page scripts (code strings). |
| `login` | `(context: LoginContext) => Promise<void>` | — | Sign in once before this scenario's pages are tested. The cookies and storage it leaves behind are used for every page. |
| `mocks` | `MockConfig[]` | — | Answers for browser requests (API fixtures). Server-side requests are not affected. |
| `include` | `string[]` | — | Only test routes matching these globs in this scenario. |
| `exclude` | `string[]` | — | Skip routes matching these globs in this scenario. |
| `query` | `Record<string, string>` | — | Query parameters added to every URL (e.g. `{ currency: 'EUR' }`). |
| `browser` | `BrowserName` | `browser.name` | Browser for this scenario. |
| `network` | `NetworkProfile` | — | Throttle the network: `"fast-3g"`, `"slow-3g"` or `{ downloadKbps, uploadKbps, latencyMs }`. Chromium throttles natively; Firefox and WebKit delay subresources instead. |
| `cpu` | `number` | — | Slow down JavaScript by this factor (Chromium only). |
| `cache` | `CacheState` | `cold` | `warm` loads the page once before testing it, like a returning visitor. |
| `clock` | `string \| number` | — | Fixed browser time (ISO string or epoch ms) for `Date.now()` / `new Date()`. A diagnostic option: the server keeps its real clock, so time-dependent output is still found. |
| `randomSeed` | `number` | — | Seed for `Math.random()` and `crypto.getRandomValues()` in the browser (diagnostic, like `clock`). |

## `matrix`

Tests every scenario in combinations of environments, so a problem that only
shows up in one locale, timezone, theme, screen size or browser is found.
Pairwise selection (the default) tests every pair of values at least once with
few combinations. The [environment matrix](https://hydration.jscrate.dev/docs/environment-matrix) guide has
the details and the limits of each browser.

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  matrix: {
    locale: ["en-US", "de-DE", "ar-EG"],
    timezoneId: ["UTC", "Asia/Karachi", "America/Los_Angeles"],
    colorScheme: ["light", "dark"],
    viewport: ["desktop", "mobile"],
    // Custom axes: feature flags, tenants, currencies
    axes: {
      checkout: {
        new: { cookies: [{ name: "flag-checkout", value: "new" }] },
        old: {},
      },
    },
  },
});
```

The first value of every axis is the baseline and is always tested.
`--no-matrix` tests the scenarios as configured.

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `locale` | `string[]` | — | Locales to test, e.g. `['en-US', 'de-DE', 'ar-EG']`. |
| `timezoneId` | `string[]` | — | Timezones to test, e.g. `['UTC', 'Asia/Karachi', 'America/Los_Angeles']`. |
| `colorScheme` | `ColorScheme[]` | — | Color schemes to test, e.g. `['light', 'dark']`. |
| `reducedMotion` | `("reduce" \| "no-preference")[]` | — | Reduced-motion settings to test. |
| `viewport` | `ViewportOption[]` | — | Viewports to test: sizes or the presets `mobile`, `tablet`, `desktop`. |
| `browser` | `BrowserName[]` | — | Browsers to test: `chromium`, `firefox`, `webkit`. |
| `network` | `NetworkProfile[]` | — | Network profiles to test: `fast` (no throttling), `fast-3g`, `slow-3g` or custom. |
| `cpu` | `number[]` | — | CPU slowdown factors (Chromium only); `1` is no slowdown. |
| `cache` | `CacheState[]` | — | Cache states to test: `cold` and `warm`. |
| `axes` | `Record<string, Record<string, ScenarioVariant>>` | — | Custom axes: axis name → value name → scenario settings. `{ flags: { 'new-checkout': { cookies: [{ name: 'flag', value: 'on' }] }, 'old-checkout': {} } }` |
| `strategy` | `"pairwise" \| "full" \| "sample"` | — | `pairwise` (default) covers every pair of values, `full` every combination, `sample` a random subset. |
| `max` | `number` | 16 | Most environments per scenario. |
| `seed` | `number` | 1 | Seed for `sample`. |
| `scenarios` | `string[]` | all | Scenarios the matrix applies to. |

## `probes`

`probes: true` (or `--probe`) proves the cause of value mismatches. Pages with
text or attribute differences are loaded again with one thing changed at a
time: the clock, the random seed, the locale, the timezone, the theme, the
viewport or storage. Each probed page costs up to 9 extra page loads, so probes
are off by default. See [probes](https://hydration.jscrate.dev/docs/probes).

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  probes: { factors: ["time", "random"], maxPages: 5 },
  // Load every page 3 times and mark findings seen in only some runs as flaky
  repeat: 3,
});
```

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

## `ready`

When a page counts as settled before the final snapshot. hydration-proof never
waits for "network idle": it waits for a quiet period without DOM changes or
React commits. For pages with animations, polling or live data that never go
quiet, set a `selector` or a `function`, or lower `quietMs`.

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  ready: {
    quietMs: 400,
    selector: "[data-page-ready]",
    // A page function, as a string, that must return a truthy value
    function: "() => window.__APP_READY__ === true",
  },
});
```

A route can override these with its own `ready` (see [routes](https://hydration.jscrate.dev/docs/routes)).

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `quietMs` | `number` | 400 | Quiet time (ms) without DOM changes or React commits before the page counts as settled. |
| `timeout` | `number` | 30000 | Maximum time (ms) per page. |
| `hydrationTimeout` | `number` | 15000 | Maximum time (ms) for hydration to finish once React is loaded. |
| `selector` | `string` | — | A selector that must exist before the final snapshot. |
| `function` | `string` | — | Page function source that must return truthy before the final snapshot. |

## `checks`

Turns individual checks on or off. Everything that compares the server HTML
with the hydrated page is on by default. The interaction and navigation checks
load each page several extra times, so they are off until you enable them; see
[interactions and navigation](https://hydration.jscrate.dev/docs/interactions), which also covers the
top-level `interactions` option for custom Playwright steps.

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  checks: {
    // Also flag suppressHydrationWarning that hides nothing
    suppressedWarnings: "strict",
    interactions: true,
    navigation: { from: "/", maxRoutes: 10 },
  },
});
```

| Check                | Finds                                                                                                                                               |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `reactErrors`        | Errors and warnings React reports (HP2xxx)                                                                                                          |
| `domDiff`            | DOM differences React produced while hydrating, in the root and in every Suspense boundary (HP1xxx), and `<head>` values hydration changed (HP1014) |
| `propsAudit`         | Attributes and text that differ from what React renders on the client, including the ones React never reports, and events handled twice (HP5006)    |
| `invalidHtml`        | Markup the browser repairs, duplicate ids and useId collisions between React roots (HP3xxx)                                                         |
| `externalChanges`    | Changes made by other scripts before hydration (HP4xxx)                                                                                             |
| `suppressedWarnings` | Differences hidden by `suppressHydrationWarning` (`"info"`), plus unused suppression (`"strict"`)                                                   |

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `reactErrors` | `boolean` | true | Collect React hydration errors and warnings. |
| `domDiff` | `boolean` | true | Compare the DOM before and after hydration. |
| `propsAudit` | `boolean` | true | Compare attributes and text with what React renders on the client. |
| `invalidHtml` | `boolean` | true | Check the server HTML for markup the browser has to repair. |
| `externalChanges` | `boolean` | true | Detect changes made by other scripts before hydration. |
| `suppressedWarnings` | `"off" \| "info" \| "strict"` | `"info"` | How to treat suppressHydrationWarning: report suppressed differences as info, or also flag unused suppression. |
| `interactions` | `boolean` | false | Type, click, focus and scroll while the page loads and check nothing is lost. |
| `navigation` | `boolean \| NavigationConfig` | false | Compare client-side navigation to each route with loading it directly (Next.js). |

## `ignore`

Findings to leave out. Ignored findings stay in the report, marked as ignored,
and do not fail the run. An `issues` rule needs a `reason`, and a rule past its
`expires` date fails the run, so a temporary exception cannot become permanent.
[Ignoring findings](https://hydration.jscrate.dev/docs/ignoring) covers when to use each kind.

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  ignore: {
    // Third-party markup that is expected to differ
    selectors: ["#ad-slot", "[data-chat-widget]"],
    attributes: [/^data-gtm-/],
    issues: [
      {
        code: "HP4001",
        route: "/checkout",
        reason: "The payment iframe rewrites its container. Ticket ACME-431.",
        expires: "2026-12-31",
      },
    ],
  },
});
```

`[data-hydration-proof-ignore]` is always in `selectors`, so you can also mark
an element in your markup.

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `selectors` | `string[]` | — | Elements whose subtree is not compared. `[data-hydration-proof-ignore]` is always included. |
| `attributes` | `(string \| RegExp)[]` | — | Attribute names (or patterns) that are never compared. |
| `textPatterns` | `RegExp[]` | — | Text differences are ignored when both values are equal after removing these patterns. |
| `issues` | `IgnoreRule[]` | — | Ignore specific findings. |

## `ci`

When the run fails: the lowest failing severity, a warning limit, a baseline of
accepted findings, budgets and a history file for trends.
[Baselines and budgets](https://hydration.jscrate.dev/docs/baselines) explains how to adopt hydration-proof
on an app that already has findings.

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  ci: {
    failOn: "warning",
    maxWarnings: 10,
    newIssuesOnly: true,
    budget: {
      warning: 5,
      codes: { HP1004: 3 },
      routes: { "/legacy/**": { error: 2 } },
    },
    history: true,
  },
});
```

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

## `hooks`

Code that runs around the test run, after the app is up. `setup` runs once per
build mode, before any page is tested and before the scenario logins, and may
return a teardown function. Teardown functions always run, even when testing
fails. An error in `setup` stops the run. There are only these two hooks; sign-in
belongs in a scenario's `login`.

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  hooks: {
    // Seed data or create users. May return a teardown function.
    setup: async ({ baseUrl }) => {
      await fetch(`${baseUrl}/api/test/seed`, { method: "POST" });
      return async () => {
        await fetch(`${baseUrl}/api/test/reset`, { method: "POST" });
      };
    },
  },
});
```

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `setup` | `(context: HookContext) => Promise<void \| (() => Promise<void> \| void)> \| void \| (() => Promise<void> \| void)` | — | Runs once after the app is up and before any page is tested (seed a database, create users). May return a teardown function. |
| `teardown` | `(context: HookContext) => Promise<void> \| void` | — | Runs once after all pages are tested. |

## `owners`

Who owns a finding. Owners come from route globs in the config and from the
CODEOWNERS entries of the source files, and they are shown in every report and
can be filtered in the HTML report.

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  owners: {
    routes: { "/checkout/**": ["@acme/payments"] },
    codeowners: true,
  },
});
```

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

## Other options

The table lists every top-level option, including the ones above:

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `$schema` | `string` | — | JSON Schema for editor completion in a JSON config, e.g. `./node_modules/hydration-proof/schema/config.json`. |
| `configVersion` | `1` | — | Config format version. |
| `adapter` | `"auto" \| "next" \| "react-router" \| "remix" \| "astro" \| "vite" \| "node" \| "none" \| (string & {}) \| Adapter` | `"auto"` | Framework adapter. `"auto"` detects it from `package.json` and config files; or name one: `"next"`, `"react-router"`, `"remix"`, `"astro"`, `"vite"`, `"node"`, `"none"`, a plugin adapter, or an adapter from `defineAdapter()`. |
| `plugins` | `HydrationProofPlugin[]` | — | Plugins: adapters, normalizers, cause detectors, reporters and route providers. |
| `server` | `ServerConfig` | — | How the app is built and started, or the URL of one already running. See [server](https://hydration.jscrate.dev/docs/configuration#server). |
| `routes` | `RoutesConfig` | — | Which pages are tested: discovered, listed, from the sitemap or crawled. See [routes](https://hydration.jscrate.dev/docs/routes). |
| `scenarios` | `ScenarioConfig[]` | one scenario named `default`, using this machine's locale and timezone and a light color scheme | Environments every route is tested in: locale, timezone, theme, viewport, cookies, signed-in state. See [scenarios](https://hydration.jscrate.dev/docs/scenarios). |
| `ready` | `ReadyConfig` | — | When a page counts as settled before the final snapshot. See [ReadyConfig](https://hydration.jscrate.dev/docs/configuration#ready). |
| `browser` | `BrowserConfig` | `{ name: "chromium", headless: true }` | Which browser runs the pages. |
| `workers` | `number` | half the CPU cores, at most 4 | Pages tested in parallel. |
| `retries` | `number` | 0 (1 on CI) | Retries for pages that failed to load. |
| `checks` | `ChecksConfig` | — | Turn individual checks on or off. See [checks](https://hydration.jscrate.dev/docs/configuration#checks). |
| `ignore` | `IgnoreConfig` | — | Selectors, attributes, text patterns and findings to leave out. See [ignoring findings](https://hydration.jscrate.dev/docs/ignoring). |
| `reporters` | `ReporterName[]` | `["list", "json", "html"]`, plus `github` inside GitHub Actions | Output formats: `list`, `json`, `html`, `junit`, `sarif`, `github`, `gitlab`. See [reports](https://hydration.jscrate.dev/docs/reports). |
| `outputDir` | `string` | `.hydration-proof/report` | Where reports are written. |
| `screenshots` | `"failures" \| "all" \| "off"` | `"failures"` when the `html` reporter is on, otherwise `"off"` | Screenshots for the HTML report: of failing pages (`"failures"`), of every page (`"all"`), or none (`"off"`). |
| `ci` | `CiConfig` | — | When the run fails: severity, baselines, budgets and history. See [baselines and budgets](https://hydration.jscrate.dev/docs/baselines). |
| `hooks` | `HooksConfig` | — | Code that runs once before the first page (`setup`) and after the last (`teardown`). |
| `cache` | `boolean` | true | Cache discovered routes between runs (keyed by build). |
| `matrix` | `MatrixConfig` | — | Test every scenario in combinations of environments. |
| `probes` | `boolean \| ProbesConfig` | false | Prove causes: pages with value mismatches are loaded again with one thing changed (clock, random seed, locale, timezone, theme, viewport, storage). |
| `repeat` | `number` | 1 | Load every page this many times and report flaky issues. |
| `interactions` | `InteractionConfig[]` | — | Custom interactions to run on routes (before or after hydration). |
| `owners` | `OwnersConfig` | — | Who owns findings: route owners and CODEOWNERS. |
| `redact` | `boolean \| RedactConfig` | true | Remove secrets and personal data from reports. |
| `projects` | `(string \| ProjectConfig)[]` | — | Monorepo: test these projects (each has its own config) in one run. |
| `sourceOrigins` | `string[]` | — | Extra origins scripts and source maps may be fetched from, for apps that serve assets from a CDN. The app's own origin is always allowed. |

### `adapter` and `plugins`

`adapter` is `"auto"` by default: it is chosen from `package.json` and the
framework's config files. Name one to override it, or pass your own from
`defineAdapter()`. Plugins add normalizers, cause detectors, route providers,
reporters and adapters. See [adapters](https://hydration.jscrate.dev/docs/adapters) and
[plugins](https://hydration.jscrate.dev/docs/plugins).

### `browser`

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | `BrowserName` | `"chromium"` | `chromium`, `firefox` or `webkit`. |
| `channel` | `string` | — | Installed browser channel, e.g. `chrome` or `msedge`. |
| `headless` | `boolean` | `true` (`--headed` turns it off) | Run the browser without a window. |

### `redact`

Redaction is on by default: emails, tokens, card numbers and secret URL
parameters are removed from every report. Add patterns, black out elements in
screenshots, or set `redact: false` to keep everything. See
[security](https://hydration.jscrate.dev/docs/security).

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `builtIn` | `boolean` | true | Remove emails, tokens, card numbers and secret URL parameters. |
| `patterns` | `RegExp[]` | — | More text to remove from reports. |
| `selectors` | `string[]` | — | Elements to black out in screenshots. |

### `projects`

A config at the root of a monorepo can list the apps to test. Each app keeps its
own config, and the run writes one combined report. See
[monorepos](https://hydration.jscrate.dev/docs/monorepos).

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  projects: ["apps/web", { path: "apps/admin", name: "admin" }],
});
```

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `path` (required) | `string` | — | Folder of the project (with its own hydration-proof config). |
| `name` | `string` | the folder name | Name shown in reports. |
| `config` | `string` | found automatically | Config file inside the folder. |

### `sourceOrigins`

To report a `file:line`, hydration-proof downloads the page's scripts and their
source maps, but only from the app's own origin. If your app serves its bundles
from a CDN, allow that origin too:

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  sourceOrigins: ["https://cdn.example.com"],
});
```

Findings are still reported when a source map is skipped, only without a
source location.

## Related

- [The CLI](https://hydration.jscrate.dev/docs/cli): flags that override these options
- [Test every route](https://hydration.jscrate.dev/docs/routes): discovery, dynamic values and globs
- [Scenarios and signed-in pages](https://hydration.jscrate.dev/docs/scenarios)
- [Ignore known findings](https://hydration.jscrate.dev/docs/ignoring) with a reason and an expiry date
- [Run it in CI](https://hydration.jscrate.dev/docs/ci)
