ESLint Flat Config: Migrating from .eslintrc to eslint.config.mjs
ESLint's flat config (v9+) replaces brittle .eslintrc hierarchies with explicit, programmatic JavaScript module configuration, offering clearer scope and better TypeScript integration.
21 Aug 2025, 23:06 UTC

The Problem with Cascading Configs
Managing ESLint via the traditional .eslintrc.* hierarchy—relying on file discovery, extends strings, and overrides blocks—becomes brittle. As projects scale, tracking which rule applies where, and ensuring consistent plugin integration, turns into maintenance debt.
The Takeaway: Programmatic Clarity
ESLint's flat config system, introduced in v8.21.0 and stabilized in v9, replaces this hierarchy with a single, array-based configuration file (e.g., eslint.config.js or eslint.config.mjs). This allows configuration to be defined programmatically using JavaScript modules, offering superior type safety and explicit control.
Explicit Configuration Over Implicit Inheritance
In the old system, extends was a string that ESLint had to resolve against a filesystem. In flat config, you explicitly import plugins and parsers as JavaScript modules. This eliminates ambiguity.
Config Structure and Scoping
Instead of global settings modified by overrides, flat config uses an array of configuration objects. Each object can define its scope using the files and ignores keys, directly replacing the functionality of .eslintignore but scoped to that specific config entry.
Example: TypeScript Setup
For a TypeScript project, the configuration moves from relying on parserOptions strings to direct module imports within languageOptions. This allows the parser to be typed correctly.
// eslint.config.mjs
import typescriptEslintParser from '@typescript-eslint/parser';
import globals from 'globals';
export default [
{
files: ['**/*.ts', '**/*.tsx'],
languageOptions: {
parser: typescriptEslintParser,
globals: { ...globals.node },
parserOptions: {
project: './tsconfig.json'
}
},
rules: {
'@typescript-eslint/no-unused-vars': 'error'
}
}
];Trade-off: Plugin Compatibility
The primary limitation is that older community plugins may not yet export a flat-config-compatible entry point. You must verify each plugin's documentation to ensure it supports direct module import rather than string-based resolution.
Verification Check
To confirm your setup loads correctly, run npx eslint . --debug from your project root. Examine the output to ensure the intended configuration object is being applied to your target files.
Migrating requires verifying Node.js 18.18.0+ and checking plugin compatibility before relying on the new structure.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.