ESLint 23 🛡️ FlatCompat — Using legacy configs in flat config
FlatCompat is the bridge between the old and new ESLint worlds. It is a class exported from the @eslint/eslintrc package that translates legacy .eslintrc-style configurations—shareable configs, extends strings, env blocks, and plugin name arrays—into the array-of-objects format that flat config expects . The translation happens at runtime, inside the eslint.config.js file, and the result is spread into the exported array. The utility exists because the flat config system removed the string-based resolution that the legacy system relied on, and the ecosystem of shareable configs and plugins has not fully migrated to the flat format.
The most common use case is a project that depends on a shareable config that has not yet shipped a flat config export. The eslint-config-next preset is the canonical example: the Next.js codemod generates a FlatCompat bridge because the preset still exports the legacy format . The Airbnb config is another example: a project that uses airbnb-base and airbnb-typescript/base must use FlatCompat because those configs do not support flat config directly . The utility is the migration-period tool, and the bridge stays in place until the dependency ships a first-class flat export.
Key point: FlatCompat translates legacy config strings into flat config objects. The class is instantiated with a baseDirectory, and its methods—extends(), env(), plugins(), and config()—return arrays of flat config objects that are spread into the exported config. The recommendedConfig and allConfig options are required when the legacy config references eslint:recommended or eslint:all.
Why FlatCompat exists
The string-resolution removal. The legacy system resolved "eslint:recommended", "plugin:react/recommended", and "airbnb-base" by name. The flat config system has no string resolution. A config is an object, and a plugin is a module. FlatCompat restores the string resolution by parsing the legacy strings and translating them into the corresponding objects . The extends() method is the most common entry point: it accepts the same strings that the legacy extends array accepted, and it returns the flat config objects that those strings represent.
The recommendedConfig requirement. A legacy config that extends "eslint:recommended" cannot be translated without a reference to the recommended config object. FlatCompat requires the recommendedConfig option for this case, and the option is set to js.configs.recommended from @eslint/js . Without it, the constructor throws a TypeError stating that the parameter is missing. The allConfig option is required for the "eslint:all" case, with js.configs.all as the value.
The environment translation. The legacy env property declared a set of globals for a runtime—browser, node, mocha. The flat config system has no env property; globals are declared explicitly in languageOptions.globals. FlatCompat provides an env() method that translates the legacy env names into the corresponding globals objects . The method is the bridge for the env property, and it is used alongside extends() when the legacy config declares environments.
The trade-off. FlatCompat is a runtime translation, not a permanent configuration. The translated objects are the same objects that a native flat config would produce, but the translation adds a dependency and a layer of indirection. The ESLint team describes the utility as a migration tool, and the configuration migrator uses it aggressively to produce a working flat config that the developer can then simplify . The bridge is meant to be removed when the dependencies ship flat configs.
a. The basic setup and the extends translation
The class is instantiated with a baseDirectory, which is the directory that the legacy config’s relative paths are resolved against. In an ESM config file, the __dirname equivalent is computed from import.meta.url.
// eslint.config.mjs — the FlatCompat setup
import { FlatCompat } from "@eslint/eslintrc";
import js from "@eslint/js";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
// The recommendedConfig is required when the legacy config
// references "eslint:recommended".
recommendedConfig: js.configs.recommended,
// The allConfig is required when it references "eslint:all".
allConfig: js.configs.all,
});
export default [
// The extends() method translates the legacy strings.
// The result is an array of flat config objects.
...compat.extends("airbnb-base", "airbnb-typescript/base"),
// The rest of the flat config follows.
{
rules: { semi: ["warn", "always"] },
},
];
The extends() method accepts multiple strings, just like the legacy extends array. The "airbnb-base" string is resolved to the eslint-config-airbnb-base package, and the "airbnb-typescript/base" string is resolved to the eslint-config-airbnb-typescript package. The translation is the same resolution that the legacy system performed, and the resulting objects are the flat config equivalents .
b. The env, plugins, and config methods
The other methods cover the remaining legacy properties. The env() method translates the environment names, the plugins() method translates the plugin names, and the config() method translates an entire legacy config object.
// eslint.config.mjs — the env and plugins methods
import { FlatCompat } from "@eslint/eslintrc";
import js from "@eslint/js";
const compat = new FlatCompat({
baseDirectory: import.meta.dirname,
recommendedConfig: js.configs.recommended,
});
export default [
...compat.extends("airbnb-base"),
// The env() method translates the legacy env names.
// The result declares the corresponding globals.
...compat.env({
browser: true,
node: true,
}),
// The plugins() method translates the plugin names.
// The result is a flat config object with the plugin modules.
...compat.plugins("react", "jsx-a11y"),
];
The config() method is the most flexible. It accepts a legacy config object and translates all of its properties—extends, env, plugins, rules, parserOptions—into the flat config format . The method is useful when the legacy config is a local object rather than a shareable config string.
// eslint.config.mjs — the config method
export default [
...compat.config({
extends: ["airbnb-base"],
env: { browser: true, node: true },
plugins: ["react"],
rules: { semi: "error" },
}),
];
Complete Example Session
// ============================================
// PART 1: THE PROBLEM — A LEGACY SHAREABLE CONFIG
// ============================================
// The project uses eslint-config-airbnb-base.
// The config has not shipped a flat config export.
// A native flat config import fails:
// import airbnb from "eslint-config-airbnb-base"; // ❌
// ============================================
// PART 2: THE FLATCOMPAT SETUP
// ============================================
// eslint.config.mjs
import { FlatCompat } from "@eslint/eslintrc";
import js from "@eslint/js";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all,
});
// ============================================
// PART 3: THE EXTENDS TRANSLATION
// ============================================
export default [
// The legacy string is translated by FlatCompat.
...compat.extends("airbnb-base"),
// The flat config rules follow.
{
rules: { semi: ["warn", "always"] },
},
];
// ============================================
// PART 4: THE ENV TRANSLATION
// ============================================
export default [
...compat.extends("airbnb-base"),
// The legacy env names become globals.
...compat.env({ browser: true, node: true }),
];
// ============================================
// PART 5: THE PLUGINS TRANSLATION
// ============================================
export default [
...compat.plugins("react", "jsx-a11y"),
{
rules: { "react/jsx-uses-react": "error" },
},
];
// ============================================
// PART 6: THE CONFIG METHOD
// ============================================
export default [
...compat.config({
extends: ["airbnb-base"],
env: { browser: true, node: true },
plugins: ["react"],
rules: { semi: "error" },
}),
];
// ============================================
// PART 7: THE RECOMMENDED CONFIG REQUIREMENT
// ============================================
// ❌ This throws a TypeError:
// const compat = new FlatCompat({ baseDirectory: __dirname });
// ...compat.extends("eslint:recommended");
//
// The error: "Missing parameter 'recommendedConfig' in FlatCompat constructor"
//
// ✅ The fix:
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
// ============================================
// PART 8: THE TYPESCRIPT-ESLINT COMBINATION
// ============================================
// A project that uses both typescript-eslint and a legacy config:
import tseslint from "typescript-eslint";
export default tseslint.config(
js.configs.recommended,
...compat.extends("airbnb-base"),
...tseslint.configs.recommendedTypeChecked,
// The FlatCompat result is spread into the tseslint.config array.
);
// ============================================
// PART 9: THE VERIFICATION
// ============================================
// npx eslint . --print-config src/index.js
//
// The command prints the resolved config.
// It is the diagnostic for a FlatCompat translation
// that produced an unexpected result.
// ============================================
// PART 10: THE CHECKLIST
// ============================================
// [ ] The @eslint/eslintrc package is installed.
// [ ] The FlatCompat class is imported.
// [ ] The baseDirectory is set.
// [ ] The recommendedConfig is set if "eslint:recommended" is used.
// [ ] The extends() method is used for the legacy shareable configs.
// [ ] The env() method is used for the legacy env properties.
// [ ] The plugins() method is used for the legacy plugin names.
// [ ] The result is spread into the exported array.
// [ ] The config is verified with --print-config.
The ten parts covered the problem, the setup, the extends translation, the env translation, the plugins translation, the config method, the recommendedConfig requirement, the TypeScript combination, the verification, and the checklist. The through-line is that FlatCompat translates the legacy string-based properties into flat config objects.
Quick Reference
The FlatCompat methods
| Method | Legacy equivalent | Returns |
|---|---|---|
extends(...strings) | extends: [...] | Array of flat config objects |
env(object) | env: {...} | Array of flat config objects |
plugins(...strings) | plugins: [...] | Array of flat config objects |
config(object) | Entire legacy config | Array of flat config objects |
The constructor options
| Option | Required | Purpose |
|---|---|---|
baseDirectory | Recommended | The directory for relative resolution |
resolvePluginsRelativeTo | Optional | The directory for plugin resolution |
recommendedConfig | Required if eslint:recommended is used | The js.configs.recommended object |
allConfig | Required if eslint:all is used | The js.configs.all object |
The common errors
| Error | Cause | Fix |
|---|---|---|
Missing parameter 'recommendedConfig' | eslint:recommended used without the option | Set recommendedConfig: js.configs.recommended |
Missing parameter 'allConfig' | eslint:all used without the option | Set allConfig: js.configs.all |
| The plugin is not resolved | The plugin package is not installed | Install the plugin package |
Best Practices
✅ Do This:
// Set the baseDirectory
const compat = new FlatCompat({ baseDirectory: __dirname }); // ✅
// Set the recommendedConfig
const compat = new FlatCompat({ recommendedConfig: js.configs.recommended }); // ✅
// Spread the result into the array
export default [...compat.extends("airbnb-base"), { rules: {} }]; // ✅
// Use the config method for a local legacy object
...compat.config({ extends: ["airbnb-base"] }); // ✅
❌ Don’t Do This:
// Omit the recommendedConfig
new FlatCompat({ baseDirectory: __dirname }).extends("eslint:recommended"); // ❌
// Use FlatCompat for a config that has a flat export
import myConfig from "my-config"; // the native import is better // ❌
// Leave the bridge in place after the dependency ships a flat export
// The bridge is a migration tool, not a permanent solution. // ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
TypeError: Missing parameter 'recommendedConfig' | eslint:recommended is used | Set recommendedConfig: js.configs.recommended |
| The legacy config does not apply | The result is not spread into the array | Use the spread operator (...) |
| The plugin is not found | The plugin package is not installed | Install the plugin package |
| The env globals are not declared | The env() method is not used | Use ...compat.env({ ... }) |
Real-World Examples
1. The basic setup
const compat = new FlatCompat({ baseDirectory: __dirname, recommendedConfig: js.configs.recommended });
2. The extends translation
...compat.extends("airbnb-base")
3. The env translation
...compat.env({ browser: true, node: true })
4. The plugins translation
...compat.plugins("react", "jsx-a11y")
5. The config method
...compat.config({ extends: ["airbnb-base"], env: { browser: true } })
6. The recommended config
recommendedConfig: js.configs.recommended
7. The all config
allConfig: js.configs.all
8. The Next.js bridge
...compat.extends("next/core-web-vitals")
9. The TypeScript combination
tseslint.config(js.configs.recommended, ...compat.extends("airbnb-base"))
10. The verification
npx eslint . --print-config src/index.js
Visual
The FlatCompat Bridge
┌──────────────────────────────────────────────────────────────┐
│ LEGACY STRING → FLAT CONFIG OBJECT │
│ │
│ "airbnb-base" │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ compat.extends() │ Translates the string │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ { plugins: {...}, │ The flat config object │
│ │ rules: {...} } │ │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ export default [ ...compat.extends("airbnb-base") ] │
│ │
│ The result is spread into the exported array. │
└──────────────────────────────────────────────────────────────┘
The recommendedConfig Requirement
┌──────────────────────────────────────────────────────────────┐
│ WHY recommendedConfig IS REQUIRED │
│ │
│ "eslint:recommended" is a legacy string. │
│ The flat config system has no string resolution. │
│ FlatCompat needs the actual object to translate the string.│
│ │
│ const compat = new FlatCompat({ │
│ recommendedConfig: js.configs.recommended, ← the object │
│ }); │
│ │
│ Without it: TypeError. │
└──────────────────────────────────────────────────────────────┘
The config Method
┌──────────────────────────────────────────────────────────────┐
│ THE config METHOD TRANSLATES THE WHOLE OBJECT │
│ │
│ compat.config({ │
│ extends: ["airbnb-base"], │
│ env: { browser: true }, │
│ plugins: ["react"], │
│ rules: { semi: "error" }, │
│ }) │
│ │ │
│ ▼ │
│ The same result as calling extends(), env(), and plugins() │
│ separately, but in a single call. │
└──────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Package | @eslint/eslintrc |
| Class | FlatCompat |
| Main methods | extends(), env(), plugins(), config() |
recommendedConfig | Required for eslint:recommended |
allConfig | Required for eslint:all |
baseDirectory | The directory for relative resolution |
| Use case | Legacy configs that have not shipped flat exports |
Key takeaways:
FlatCompattranslates legacy config strings into flat config objects. Theextends()method accepts the same strings that the legacyextendsarray accepted, and it returns the flat config objects that those strings represent. The result is spread into the exported array .- The
recommendedConfigoption is required wheneslint:recommendedis used. The flat config system has no string resolution, soFlatCompatneeds the actual recommended config object to translate the string. The option is set tojs.configs.recommendedfrom@eslint/js. - The
env()andplugins()methods translate the other legacy properties. Theenv()method converts the environment names to globals, and theplugins()method converts the plugin names to plugin modules. Theconfig()method translates an entire legacy config object in one call . FlatCompatis a migration tool, not a permanent solution. The ESLint team describes it as a bridge, and the configuration migrator uses it aggressively to produce a working flat config that the developer can then simplify . The bridge is meant to be removed when the dependencies ship flat configs.- The
baseDirectoryis the resolution anchor. The option tellsFlatCompatwhere to resolve the legacy config’s relative paths. In an ESM file, the__dirnameequivalent is computed fromimport.meta.url.
Remember: FlatCompat is the bridge between the legacy string-based config system and the flat config object system. It translates extends strings, env names, and plugins arrays into the flat config format. The recommendedConfig option is required for eslint:recommended, and the baseDirectory anchors the resolution. The utility is a migration tool, and the bridge is meant to be temporary. A native flat config import is always preferable to a FlatCompat translation, and the bridge should be removed when the dependency ships a flat export.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!