# Config files

Configure React Doctor with a `doctor.config.*` file or the `reactDoctor` key in `package.json`. This page explains where React Doctor looks, which config wins, and how each common option changes a scan.

## Where React Doctor looks

React Doctor starts in the scanned directory and walks up until it reaches `.git` or a monorepo root. Config files can use `.ts`, `.mts`, `.cts`, `.js`, `.mjs`, `.cjs`, `.json`, or `.jsonc`. You can also put the same object under `reactDoctor` in `package.json`.

Use one config per directory. If more than one exists, React Doctor uses the first valid config it finds and ignores lower-priority fallbacks. Local config wins over ancestor config.

JSON config files use JSON5, so comments and trailing commas are allowed. Add `$schema: "https://react.doctor/schema/config.json"` for editor autocomplete.

Command-line flags override config values.

## Example config file

```ts
// doctor.config.ts
import { defineConfig } from "react-doctor/api";

export default defineConfig({
  ignore: {
    rules: ["react-doctor/no-danger"],
    files: ["src/generated/**"],
    overrides: [
      {
        files: ["components/search/HighlightedSnippet.tsx"],
        rules: ["react-doctor/no-danger"],
      },
    ],
  },
  rules: {
    "react-doctor/no-array-index-as-key": "error",
  },
  categories: {
    Maintainability: "warn",
  },
});
```

Use `doctor.config.json` for JSON config:

```json
{
  "$schema": "https://react.doctor/schema/config.json",
  "ignore": {
    "rules": ["react-doctor/no-danger"],
    "files": ["src/generated/**"],
    "overrides": [
      {
        "files": ["components/search/HighlightedSnippet.tsx"],
        "rules": ["react-doctor/no-danger"]
      }
    ]
  },
  "rules": {
    "react-doctor/no-array-index-as-key": "error"
  },
  "categories": {
    "Maintainability": "warn"
  }
}
```

## Common keys

Use these keys to control scan scope, rule severity, output surfaces, and project-specific escape hatches.

<table>
  <thead>
    <tr>
      <th>Key</th>
      <th>Type</th>
      <th>Default</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        <code>ignore.rules</code>
      </td>
      <td>
        <code>string[]</code>
      </td>
      <td>
        <code>[]</code>
      </td>
      <td>Drop matching rule diagnostics after linting</td>
    </tr>
    <tr>
      <td>
        <code>ignore.files</code>
      </td>
      <td>
        <code>string[]</code>
      </td>
      <td>
        <code>[]</code>
      </td>
      <td>Exclude matching files from scanning</td>
    </tr>
    <tr>
      <td>
        <code>ignore.overrides</code>
      </td>
      <td>
        <code>{`{ files, rules? }[]`}</code>
      </td>
      <td>
        <code>[]</code>
      </td>
      <td>Drop diagnostics for matching files or rules</td>
    </tr>
    <tr>
      <td>
        <code>lint</code>
      </td>
      <td>
        <code>boolean</code>
      </td>
      <td>
        <code>true</code>
      </td>
      <td>Enable lint diagnostics</td>
    </tr>
    <tr>
      <td>
        <code>supplyChain</code>
      </td>
      <td>
        <code>{`{ enabled?, minScore?, severity?, includeDevDependencies? }`}</code>
      </td>
      <td>
        <code>{`{ enabled: true, minScore: 50, severity: "error", includeDevDependencies: true }`}</code>
      </td>
      <td>Configure Socket.dev checks for direct dependencies</td>
    </tr>
    <tr>
      <td>
        <code>warnings</code>
      </td>
      <td>
        <code>boolean</code>
      </td>
      <td>
        <code>true</code>
      </td>
      <td>Surface warning-severity diagnostics</td>
    </tr>
    <tr>
      <td>
        <code>verbose</code>
      </td>
      <td>
        <code>boolean</code>
      </td>
      <td>
        <code>false</code>
      </td>
      <td>Show more detail</td>
    </tr>
    <tr>
      <td>
        <code>scope</code>
      </td>
      <td>
        <code>{`"full" | "files" | "changed" | "lines"`}</code>
      </td>
      <td>
        <code>full</code>
      </td>
      <td>Choose how much source to scan and report</td>
    </tr>
    <tr>
      <td>
        <code>base</code>
      </td>
      <td>
        <code>string</code>
      </td>
      <td />
      <td>Set the git ref used by partial scopes</td>
    </tr>
    <tr>
      <td>
        <code>blocking</code>
      </td>
      <td>
        <code>{`"error" | "warning" | "none"`}</code>
      </td>
      <td>
        <code>error</code>
      </td>
      <td>Severity that fails CI (exits non-zero)</td>
    </tr>
    <tr>
      <td>
        <code>customRulesOnly</code>
      </td>
      <td>
        <code>boolean</code>
      </td>
      <td>
        <code>false</code>
      </td>
      <td>Run only configured custom rules</td>
    </tr>
    <tr>
      <td>
        <code>share</code>
      </td>
      <td>
        <code>boolean</code>
      </td>
      <td>
        <code>true</code>
      </td>
      <td>Enable share URL behavior</td>
    </tr>
    <tr>
      <td>
        <code>noScore</code>
      </td>
      <td>
        <code>boolean</code>
      </td>
      <td>
        <code>false</code>
      </td>
      <td>Skip the score API</td>
    </tr>
    <tr>
      <td>
        <code>textComponents</code>
      </td>
      <td>
        <code>string[]</code>
      </td>
      <td>
        <code>[]</code>
      </td>
      <td>React Native text component aliases</td>
    </tr>
    <tr>
      <td>
        <code>rawTextWrapperComponents</code>
      </td>
      <td>
        <code>string[]</code>
      </td>
      <td>
        <code>[]</code>
      </td>
      <td>React Native string wrapper components</td>
    </tr>
    <tr>
      <td>
        <code>runtimeGlobals</code>
      </td>
      <td>
        <code>string[]</code>
      </td>
      <td>
        <code>[]</code>
      </td>
      <td>Runtime JSX globals that are not imported in the current file</td>
    </tr>
    <tr>
      <td>
        <code>serverAuthFunctionNames</code>
      </td>
      <td>
        <code>string[]</code>
      </td>
      <td>
        <code>[]</code>
      </td>
      <td>Accepted server auth guard names</td>
    </tr>
    <tr>
      <td>
        <code>respectInlineDisables</code>
      </td>
      <td>
        <code>boolean</code>
      </td>
      <td>
        <code>true</code>
      </td>
      <td>Respect inline lint disables</td>
    </tr>
    <tr>
      <td>
        <code>adoptExistingLintConfig</code>
      </td>
      <td>
        <code>boolean</code>
      </td>
      <td>
        <code>true</code>
      </td>
      <td>Merge existing JSON lint config</td>
    </tr>
    <tr>
      <td>
        <code>rootDir</code>
      </td>
      <td>
        <code>string</code>
      </td>
      <td />
      <td>Scan a different directory, resolved relative to the config file</td>
    </tr>
    <tr>
      <td>
        <code>projects</code>
      </td>
      <td>
        <code>string[]</code>
      </td>
      <td />
      <td>Scan and score each listed project separately</td>
    </tr>
    <tr>
      <td>
        <code>ignore.tags</code>
      </td>
      <td>
        <code>string[]</code>
      </td>
      <td>
        <code>[]</code>
      </td>
      <td>Disable rule families by tag before linting</td>
    </tr>
    <tr>
      <td>
        <code>rules</code>
      </td>
      <td>
        <code>object</code>
      </td>
      <td />
      <td>
        Set rule severity to <code>error</code>, <code>warn</code>, or{" "}
        <code>off</code>
      </td>
    </tr>
    <tr>
      <td>
        <code>categories</code>
      </td>
      <td>
        <code>object</code>
      </td>
      <td />
      <td>
        Set category severity to <code>error</code>, <code>warn</code>, or{" "}
        <code>off</code>
      </td>
    </tr>
    <tr>
      <td>
        <code>buckets</code>
      </td>
      <td>
        <code>object</code>
      </td>
      <td />
      <td>
        Set severity for curated buckets such as <code>compiler-cleanup</code>
      </td>
    </tr>
    <tr>
      <td>
        <code>surfaces</code>
      </td>
      <td>
        <code>object</code>
      </td>
      <td />
      <td>Control visibility on CLI, score, PR comments, and CI failure</td>
    </tr>
    <tr>
      <td>
        <code>plugins</code>
      </td>
      <td>
        <code>string[]</code>
      </td>
      <td>
        <code>[]</code>
      </td>
      <td>Load custom oxlint-shaped rule plugins</td>
    </tr>
  </tbody>
</table>

## Deprecated compatibility option

The `deadCode` boolean remains for older configurations and API callers. Setting it to `false` disables the project-level duplicated JSX check. It does not disable explicitly enabled graph rules, lint-based complexity rules, or the rest of the `Maintainability` category. Leave it unset in new configurations.

## Enable optional project graph rules

Unused code, package, and import-cycle checks require an explicit rule entry. They run only during full scans. Partial and baseline scans skip them because those scopes cannot prove whole-project reachability.

```json
{
  "$schema": "https://react.doctor/schema/config.json",
  "rules": {
    "react-doctor/unused-export": "warn",
    "react-doctor/unused-dependency": "warn",
    "react-doctor/circular-dependency": "warn"
  }
}
```

Add any subset of the six [optional project graph rules](/docs/overview/react-maintainability#optional-whole-project-graph-rules). A `Maintainability` category setting does not activate them. The explicit rule entry sets each enabled rule's severity.

Severity overrides use `error`, `warn`, or `off`. Per-rule `rules` entries win over `categories`; `buckets` currently supports `compiler-cleanup` for React Compiler cleanup rules.

Category overrides use the five buckets: `Security`, `Bugs`, `Performance`, `Accessibility`, and `Maintainability`.

Use `rules` or `categories` when you want to change severity or stop a rule from running. Use `ignore.rules` when you want the rule to run but drop its diagnostics from the final report. Use `surfaces` when you want a diagnostic to stay visible locally but disappear from PR comments, the score, or the CI failure gate.

## Configure rules from the CLI

Use the `rules` commands when you want React Doctor to edit the active config file for you:

```bash
npx react-doctor@latest rules list
npx react-doctor@latest rules explain react-doctor/no-array-index-as-key
npx react-doctor@latest rules disable react-doctor/no-array-index-as-key
npx react-doctor@latest rules set react-doctor/no-danger warn
npx react-doctor@latest rules category Performance off
npx react-doctor@latest rules ignore-tag design
```

React Doctor accepts a full rule key, a bare rule ID, or a legacy key. It updates the existing `doctor.config.*` or `package.json` config. When no config exists, it creates `doctor.config.json` with the schema URL.

Rule-level settings override category settings. Ignored tags disable matching rules before linting, so a rule-level setting cannot re-enable a tag-ignored rule. Surface controls change visibility without disabling the rule.

## Configure scan scope and blocking

Use `scope` to choose which findings React Doctor reports:

- `full`: every finding in the project
- `files`: every finding in changed files
- `changed`: only findings introduced compared with the base
- `lines`: findings whose source range touches a changed line

Set `base` when React Doctor should compare against a specific git ref. The CLI `--scope` and `--base` flags override these values. The older `diff` option remains a deprecated alias.

`blocking` controls the process exit code. Use `error` to fail on errors, `warning` to fail on any finding, or `none` for an advisory scan. The CLI defaults to `error`; the GitHub Action defaults to `none`.

## Configure dependency checks

The supply-chain check scores direct dependencies through Socket.dev. It runs by default and reports a dependency when its supply-chain or vulnerability score falls below `50`.

```json
{
  "supplyChain": {
    "enabled": true,
    "minScore": 60,
    "severity": "warning",
    "includeDevDependencies": false
  }
}
```

In partial and staged scans, React Doctor runs this network check only when `package.json` changed. Set `enabled: false` or pass `--no-supply-chain` to disable it.

## Per-project scoring in monorepos

Use `projects` in the root config when you always want a separate score per module, without passing `--project` on every run:

```json
{
  "projects": ["modules/billing", "modules/payroll"]
}
```

Entries resolve the same way as `--project` values: workspace package names first, then directory paths relative to the scanned root. `"*"` selects every discovered workspace project. Invalid entries fail the run with the same error as the flag.

Precedence: an explicit `--project` flag overrides this list. Only the config at the invocation root is consulted; `projects` inside a module's own config is ignored.

In multi-project scans, each module's own `doctor.config.*` layers additively onto the root config: `rules` and `categories` merge per key, and `ignore` lists union. A module can override one rule without discarding the shared base.

## React Native escape hatches

Use `textComponents` for components that behave like React Native's `<Text>`:

```json
{
  "textComponents": ["Typography", "NativeTabs.Trigger.Label"]
}
```

Use `rawTextWrapperComponents` for components that route string children through an internal text component:

```json
{
  "rawTextWrapperComponents": ["Button"]
}
```

## Server auth guards

Use `serverAuthFunctionNames` when your server actions use custom auth helpers:

```json
{
  "serverAuthFunctionNames": ["requireWorkspaceMember", "ensureSignedIn"]
}
```

## Existing lint config

React Doctor can adopt JSON ESLint and oxlint config automatically. Set `adoptExistingLintConfig: false` if you want React Doctor to ignore existing lint config.

## Narrow ignores and suppressions

Use the narrowest control that solves the problem. Ignore a rule everywhere only when it does not apply to your project:

```json
{
  "ignore": {
    "rules": ["react-doctor/no-danger"]
  }
}
```

Ignore generated files when every diagnostic in those files is noise:

```json
{
  "ignore": {
    "files": ["src/generated/**"]
  }
}
```

Ignore one rule in specific files when the exception is local:

```json
{
  "ignore": {
    "overrides": [
      {
        "files": ["components/search/HighlightedSnippet.tsx"],
        "rules": ["react-doctor/no-danger"]
      }
    ]
  }
}
```

Use an inline suppression when the exception should live with the code:

```tsx
// react-doctor-disable-next-line react-doctor/no-derived-state
useEffect(() => {
  setValue(initialValue);
}, []);
```

Comma-separate rule names when one line needs more than one suppression. Use `npx react-doctor@latest why src/App.tsx:42` when a suppression does not apply.

## Custom rule plugins

React Doctor can run custom oxlint-shaped plugins from your config. Plugin rules are opt-in and use the same CLI, score, PR comment, and CI surfaces as built-in rules.

```json
{
  "plugins": ["./lint/team-conventions.cjs"],
  "rules": {
    "team-conventions/no-bare-fetch": "error"
  }
}
```

For the plugin module shape, Node API path, and limits, see [Add custom rules](/docs/configuration/add-custom-rules).
