Configuration
Configuration
Flags
| Flag | Default | What it does |
|---|---|---|
react | false | React 19+ with Server Components, Hooks, JSX-A11y, Storybook, Testing Library |
node | false | Node.js globals, eslint-plugin-n rules. Disables browser compat checks. In mixed frontend/backend repos, add file-scoped overrides (see below). |
ai | false | Enforces a consistent standard across human and AI contributors (see AI Mode) |
oxlint | false | Disables 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.
Option A: directory-local configs (recommended)
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 configThis 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.