ESLint Flat Config from Zero: One File to End Team Style Wars
Flat Config is now the default for new ESLint projects, and the old `.eslintrc` format is effectively legacy. Teams that don't migrate will hit editor integration breakage and miss out on the cleaner ESM-native authoring model that makes TypeScript and monorepo setups simpler.
ESLint 9.x+ officially pushes Flat Config (`eslint.config.mjs`) over the old `.eslintrc.js` format. The new approach exports a plain array of config blocks, uses native ESM imports, and registers plugins as objects instead of strings — making the config read more like a standard JS module and cutting down on nesting. A complete starter config pulls in `@eslint/js` for recommended JS rules, `globals` to declare browser globals, and `typescript-eslint` for TS support, then layers custom rules like `no-var` (error), `no-console` (warn), and enforced double quotes, semicolons, and 2-space indentation.
The setup pairs with VSCode's ESLint extension and `editor.codeActionsOnSave` to auto-fix formatting on every save. A critical flag — `"eslint.useFlatConfig": true` — must be set, or the editor ignores the new config file entirely. Common pitfalls include getting the filename exactly right, never using `module.exports` in `.mjs`, not omitting the spread operator on `...tseslint.configs.recommended`, and always declaring `globals.browser` to avoid `no-undef` errors on `window` and `document`.
For teams, the recommendation is a single shared config, combined with Prettier for formatting, enforced via `pnpm lint` in CI, and rolled out gradually with warn-level rules on legacy codebases to avoid a wall of errors.
The Flat Config shift is as much about tooling compatibility as it is about syntax: VSCode's ESLint extension will silently ignore the new format unless a specific flag is toggled, which is an easy trap for teams upgrading.
ESLint's rule severity system (0/1/2) doubles as a rollout strategy — setting everything to `warn` on legacy projects lets a team adopt standards without blocking builds, then ratchet up to `error` over time.
The explicit `globals` declaration requirement in Flat Config surfaces a long-standing hidden assumption: old configs often worked because environments were inferred, but the new model forces teams to state exactly where their code runs.