Skip to content

Configuration

Configuration

Flags

FlagDefaultWhat it does
reactfalseReact 19+ with Server Components, Hooks, JSX-A11y, Storybook, Testing Library
nodefalseNode.js globals, eslint-plugin-n rules. Disables browser compat checks. In mixed frontend/backend repos, add file-scoped overrides (see below).
aifalseEnforces a consistent standard across human and AI contributors (see AI Mode)
oxlintfalseDisables ESLint rules that OxLint already covers (see OxLint Integration)

Flags are independent. Combine them however you need. 2^4 = 16 configs exist, all pre-built.

TypeScript always uses strictTypeChecked — the strictest typescript-eslint preset. No "recommended" fallback. This matches TypeScript's own direction toward strict-by-default.

Usage examples

// eslint.config.ts
import { getEslintConfig } from "eslint-config-setup"

export default await getEslintConfig({ react: true })

Mixed frontend/backend repositories

When you enable both react: true and node: true, environment-specific rules and globals are loaded into the shared generated config. This is intentional for the pre-built 16-permutation architecture.

For strict runtime boundaries, use directory-local eslint.config.* files and let ESLint resolve them per file:

project/
├── eslint.config.ts         # root/scripts config
├── app/
│   └── eslint.config.ts     # react config
└── convex/
    ├── package.json         # engines.node for backend runtime
    └── eslint.config.ts     # node config
// eslint.config.ts (root)
import { getEslintConfig } from "eslint-config-setup"

export default await getEslintConfig({
  node: true,
})
// app/eslint.config.ts
import { getEslintConfig } from "eslint-config-setup"

export default await getEslintConfig({
  ai: true,
  react: true,
})
// convex/eslint.config.ts
import { getEslintConfig } from "eslint-config-setup"

export default await getEslintConfig({
  ai: true,
  node: true,
})
// convex/package.json
{
  "name": "convex-backend",
  "private": true,
  "engines": {
    "node": ">=22.4.0"
  }
}

Run lint from the repo root:

eslint .

No root: true setting is needed with flat config.

eslint-plugin-n resolves the Node target from engines.node by default. Prefer this over per-rule overrides.

Option B: single config + file-scoped overrides

If you prefer one config file, add explicit file-scoped blocks:

// package.json
{
  "engines": {
    "node": ">=22.4.0"
  }
}
// eslint.config.ts
import { getEslintConfig } from "eslint-config-setup"

const config = await getEslintConfig({
  ai: true,
  react: true,
  node: true,
})

config.push(
  {
    name: "project/browser-runtime",
    files: ["app/**/*.{ts,tsx}"],
    languageOptions: {
      globals: {
        // Disable leaked Node globals in browser code
        Buffer: "off",
        __dirname: "off",
        __filename: "off",
        module: "off",
        process: "off",
        require: "off",
      },
    },
    rules: {
      "node/no-unsupported-features/node-builtins": "off",
    },
  },
  {
    name: "project/node-runtime",
    files: ["convex/**/*.ts", "scripts/**/*.ts"],
    languageOptions: {
      globals: {
        // Disable leaked browser globals in server code
        document: "off",
        localStorage: "off",
        navigator: "off",
        sessionStorage: "off",
        window: "off",
      },
    },
  },
)

export default config

This keeps the generated baseline, while making runtime boundaries explicit in your project.

See the Rule API for the full set of rule manipulation functions and scoped rules.