Skip to content

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.

NOTE

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 10 (default)
eslint .

# ESLint 9.30+ (enable per-file config lookup)
eslint --flag v10_config_lookup_from_file .

No root: true setting is needed with flat config.

For older ESLint 9 releases, use --flag unstable_config_lookup_from_file or separate --config scripts.

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.

TIP

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