Configuration Migration Guide
This guide provides an overview of how you can migrate your ESLint configuration file from the eslintrc format (typically configured in .eslintrc.js
or .eslintrc.json
files) to the new flat config format (typically configured in an eslint.config.js
file).
To learn more about the flat config format, refer to this blog post.
For reference information on these configuration formats, refer to the following documentation:
Migrate Your Config File
To get started, use the configuration migrator on your existing configuration file (.eslintrc
, .eslintrc.json
, .eslintrc.yml
), like this:
npx @eslint/migrate-config .eslintrc.json
This will create a starting point for your eslint.config.js
file but is not guaranteed to work immediately without further modification. It will, however, do most of the conversion work mentioned in this guide automatically.
Start Using Flat Config Files
The flat config file format has been the default configuration file format since ESLint v9.0.0. You can start using the flat config file format without any additional configuration.
To use flat config with ESLint v8, place a eslint.config.js
file in the root of your project or set the ESLINT_USE_FLAT_CONFIG
environment variable to true
.
Things That Haven’t Changed between Configuration File Formats
While the configuration file format has changed from eslintrc to flat config, the following has stayed the same:
- Syntax for configuring rules
- Syntax for configuring processors
- The CLI, except for the flag changes noted in CLI Flag Changes.
- Global variables are configured the same way, but on a different property (see Configuring Language Options).
Key Differences between Configuration Formats
A few of the most notable differences between the eslintrc and flat config formats are the following:
Importing Plugins and Custom Parsers
Eslintrc files use string-based import system inside the plugins
property to load plugins and inside the extends
property to load external configurations.
Flat config files represent plugins and parsers as JavaScript objects. This means you can use CommonJS require()
or ES module import
statements to load plugins and custom parsers from external files.
For example, this eslintrc config file loads eslint-plugin-jsdoc
and configures rules from that plugin:
// .eslintrc.js
module.exports = {
// ...other config
plugins: ["jsdoc"],
rules: {
"jsdoc/require-description": "error",
"jsdoc/check-values": "error"
}
// ...other config
};
In flat config, you would do the same thing like this:
// eslint.config.js
import jsdoc from "eslint-plugin-jsdoc";
export default [
{
files: ["**/*.js"],
plugins: {
jsdoc: jsdoc
},
rules: {
"jsdoc/require-description": "error",
"jsdoc/check-values": "error"
}
}
];
Custom Parsers
In eslintrc files, importing a custom parser is similar to importing a plugin: you use a string to specify the name of the parser.
In flat config files, import a custom parser as a module, then assign it to the languageOptions.parser
property of a configuration object.
For example, this eslintrc config file uses the @babel/eslint-parser
parser:
// .eslintrc.js
module.exports = {
// ...other config
parser: "@babel/eslint-parser",
// ...other config
};
In flat config, you would do the same thing like this:
// eslint.config.js
import babelParser from "@babel/eslint-parser";
export default [
{
// ...other config
languageOptions: {
parser: babelParser
}
// ...other config
}
];
Processors
In eslintrc files, processors had to be defined in a plugin, and then referenced by name in the configuration. Processors beginning with a dot indicated a file extension-named processor which ESLint would automatically configure for that file extension.
In flat config files, processors can still be referenced from plugins by their name, but they can now also be inserted directly into the configuration. Processors will never be automatically configured, and must be explicitly set in the configuration.
As an example with a custom plugin with processors:
// node_modules/eslint-plugin-someplugin/index.js
module.exports = {
processors: {
".md": {
preprocess() {},
postprocess() {}
},
"someProcessor": {
preprocess() {},
postprocess() {}
}
}
};
In eslintrc, you would configure as follows:
// .eslintrc.js
module.exports = {
plugins: ["someplugin"],
processor: "someplugin/someProcessor"
};
ESLint would also automatically add the equivalent of the following:
{
overrides: [{
files: ["**/*.md"],
processor: "someplugin/.md"
}]
}
In flat config, the following are all valid ways to express the same:
// eslint.config.js
import somePlugin from "eslint-plugin-someplugin";
export default [
{
plugins: { somePlugin },
processor: "somePlugin/someProcessor"
},
{
plugins: { somePlugin },
// We can embed the processor object in the config directly
processor: somePlugin.processors.someProcessor
},
{
// We don't need the plugin to be present in the config to use the processor directly
processor: somePlugin.processors.someProcessor
}
];
Note that because the .md
processor is not automatically added by flat config, you also need to specify an extra configuration element:
{
files: ["**/*.md"],
processor: somePlugin.processors[".md"]
}
Glob-Based Configs
By default, eslintrc files lint all files (except those covered by .eslintignore
) in the directory in which they’re placed and its child directories. If you want to have different configurations for different file glob patterns, you can specify them in the overrides
property.
By default, flat config files support different glob pattern-based configs in exported array. You can include the glob pattern in a config object’s files
property. If you don’t specify a files
property, the config defaults to the glob pattern "**/*.{js,mjs,cjs}"
. Basically, all configuration in the flat config file is like the eslintrc overrides
property.
eslintrc Examples
For example, this eslintrc file applies to all files in the directory where it is placed and its child directories:
// .eslintrc.js
module.exports = {
// ...other config
rules: {
semi: ["warn", "always"]
}
};
This eslintrc file supports multiple configs with overrides:
// .eslintrc.js
module.exports = {
// ...other config
overrides: [
{
files: ["src/**/*"],
rules: {
semi: ["warn", "always"]
}
},
{
files:["test/**/*"],
rules: {
"no-console": "off"
}
}
]
};
For flat config, here is a configuration with the default glob pattern:
// eslint.config.js
import js from "@eslint/js";
export default [
js.configs.recommended, // Recommended config applied to all files
// Override the recommended config
{
rules: {
indent: ["error", 2],
"no-unused-vars": "warn"
}
// ...other configuration
}
];
A flat config example configuration supporting multiple configs for different glob patterns:
// eslint.config.js
import js from "@eslint/js";
export default [
js.configs.recommended, // Recommended config applied to all files
// File-pattern specific overrides
{
files: ["src/**/*", "test/**/*"],
rules: {
semi: ["warn", "always"]
}
},
{
files:["test/**/*"],
rules: {
"no-console": "off"
}
}
// ...other configurations
];
Configuring Language Options
In eslintrc files, you configure various language options across the env
, globals
and parserOptions
properties. Groups of global variables for specific runtimes (e.g. document
and window
for browser JavaScript; process
and require
for Node.js) are configured with the env
property.
In flat config files, the globals
, and parserOptions
are consolidated under the languageOptions
key; the env
property doesn’t exist. Groups of global variables for specific runtimes are imported from the globals npm package and included in the globals
property. You can use the spread operator (...
) to import multiple globals at once.
For example, here’s an eslintrc file with language options:
// .eslintrc.js
module.exports = {
env: {
browser: true,
node: true
},
globals: {
myCustomGlobal: "readonly",
},
parserOptions: {
ecmaVersion: 2022,
sourceType: "module"
}
// ...other config
}
Here’s the same configuration in flat config:
// eslint.config.js
import globals from "globals";
export default [
{
languageOptions: {
ecmaVersion: 2022,
sourceType: "module",
globals: {
...globals.browser,
...globals.node,
myCustomGlobal: "readonly"
}
}
// ...other config
}
];
eslint-env
Configuration Comments
In the eslintrc config system it was possible to use eslint-env
configuration comments to define globals for a file.
These comments are no longer recognized when linting with flat config: in a future version of ESLint, eslint-env
comments will be reported as errors.
For this reason, when migrating from eslintrc to flat config, eslint-env
configuration comments should be removed from all files.
They can be either replaced with equivalent but more verbose global
configuration comments, or dropped in favor of globals
definitions in the config file.
For example, when using eslintrc, a file to be linted could look like this:
// tests/my-file.js
/* eslint-env mocha */
describe("unit tests", () => {
it("should pass", () => {
// ...
});
});
In the above example, describe
and it
would be recognized as global identifiers because of the /* eslint-env mocha */
comment.
The same effect can be achieved with flat config with a global
configuration comment, e.g.:
// tests/my-file.js
/* global describe, it -- Globals defined by Mocha */
describe("unit tests", () => {
it("should pass", () => {
// ...
});
});
Another option is to remove the comment from the file being linted and define the globals in the configuration, for example:
// eslint.config.js
import globals from "globals";
export default [
// ...other config
{
files: [
"tests/**"
],
languageOptions: {
globals: {
...globals.mocha
}
}
}
];
Predefined and Shareable Configs
In eslintrc files, use the extends
property to use predefined and shareable configs. ESLint comes with two predefined configs that you can access as strings:
"eslint:recommended"
: the rules recommended by ESLint"eslint:all"
: all rules shipped with ESLint
You can also use the extends
property to extend a shareable config. Shareable configs can either be paths to local config files or npm package names.
In flat config files, predefined configs are imported from separate modules into flat config files. The recommended
and all
rules configs are located in the @eslint/js
package. You must import this package to use these configs:
npm install @eslint/js --save-dev
You can add each of these configs to the exported array or expose specific rules from them. You must import the modules for local config files and npm package configs with flat config.
For example, here’s an eslintrc file using the built-in eslint:recommended
config:
// .eslintrc.js
module.exports = {
// ...other config
extends: "eslint:recommended",
rules: {
semi: ["warn", "always"]
},
// ...other config
}
This eslintrc file uses built-in config, local custom config, and shareable config from an npm package:
// .eslintrc.js
module.exports = {
// ...other config
extends: ["eslint:recommended", "./custom-config.js", "eslint-config-my-config"],
rules: {
semi: ["warn", "always"]
},
// ...other config
}
To use the same configs in flat config, you would do the following:
// eslint.config.js
import js from "@eslint/js";
import customConfig from "./custom-config.js";
import myConfig from "eslint-config-my-config";
export default [
js.configs.recommended,
customConfig,
myConfig,
{
rules: {
semi: ["warn", "always"]
},
// ...other config
}
];
Note that because you are just importing JavaScript modules, you can mutate the config objects before ESLint uses them. For example, you might want to have a certain config object only apply to your test files:
// eslint.config.js
import js from "@eslint/js";
import customTestConfig from "./custom-test-config.js";
export default [
js.configs.recommended,
{
...customTestConfig,
files: ["**/*.test.js"],
},
];
Using eslintrc Configs in Flat Config
You may find that there’s a shareable config you rely on that hasn’t yet been updated to flat config format. In that case, you can use the FlatCompat
utility to translate the eslintrc format into flat config format. First, install the @eslint/eslintrc
package:
npm install @eslint/eslintrc --save-dev
Then, import FlatCompat
and create a new instance to convert an existing eslintrc config. For example, if the npm package eslint-config-my-config
is in eslintrc format, you can write this:
import { FlatCompat } from "@eslint/eslintrc";
import path from "path";
import { fileURLToPath } from "url";
// mimic CommonJS variables -- not needed if using CommonJS
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname
});
export default [
// mimic ESLintRC-style extends
...compat.extends("eslint-config-my-config"),
];
This example uses the FlatCompat#extends()
method to insert the eslint-config-my-config
into the flat config array.
For more information about the FlatCompat
class, please see the package README.
Ignoring Files
With eslintrc, you can make ESLint ignore files by creating a separate .eslintignore
file in the root of your project. The .eslintignore
file uses the same glob pattern syntax as .gitignore
files. Alternatively, you can use an ignorePatterns
property in your eslintrc file.
To ignore files with flat config, you can use the ignores
property in a config object with no other properties. The ignores
property accepts an array of glob patterns. Flat config does not support loading ignore patterns from .eslintignore
files, so you’ll need to migrate those patterns directly into flat config.
For example, here’s a .eslintignore
example you can use with an eslintrc config:
# .eslintignore
temp.js
config/*
# ...other ignored files
Here are the same patterns represented as ignorePatterns
in a .eslintrc.js
file:
// .eslintrc.js
module.exports = {
// ...other config
ignorePatterns: ["temp.js", "config/*"],
};
The equivalent ignore patterns in flat config look like this:
export default [
// ...other config
{
// Note: there should be no other properties in this object
ignores: ["**/temp.js", "config/*"]
}
];
In .eslintignore
, temp.js
ignores all files named temp.js
, whereas in flat config, you need to specify this as **/temp.js
. The pattern temp.js
in flat config only ignores a file named temp.js
in the same directory as the configuration file.
Linter Options
Eslintrc files let you configure the linter itself with the noInlineConfig
and reportUnusedDisableDirectives
properties.
The flat config system introduces a new top-level property linterOptions
that you can use to configure the linter. In the linterOptions
object, you can include noInlineConfig
and reportUnusedDisableDirectives
.
For example, here’s an eslintrc file with linter options enabled:
// .eslintrc.js
module.exports = {
// ...other config
noInlineConfig: true,
reportUnusedDisableDirectives: true
}
Here’s the same options in flat config:
// eslint.config.js
export default [
{
// ...other config
linterOptions: {
noInlineConfig: true,
reportUnusedDisableDirectives: "warn"
}
}
];
CLI Flag Changes
The following CLI flags are no longer supported with the flat config file format:
--rulesdir
--ext
--resolve-plugins-relative-to
The flag --no-eslintrc
has been replaced with --no-config-lookup
.
--rulesdir
The --rulesdir
flag was used to load additional rules from a specified directory. This is no longer supported when using flat config. You can instead create a plugin containing the local rules you have directly in your config, like this:
// eslint.config.js
import myRule from "./rules/my-rule.js";
export default [
{
// define the plugin
plugins: {
local: {
rules: {
"my-rule": myRule
}
}
},
// configure the rule
rules: {
"local/my-rule": ["error"]
}
}
];
--ext
The --ext
flag was used to specify additional file extensions ESLint should search for when a directory was passed on the command line, such as npx eslint .
. This is no longer supported when using flat config. Instead, specify the file patterns you’d like ESLint to search for directly in your config. For example, if you previously were using --ext .ts,.tsx
, then you will need to update your config file like this:
// eslint.config.js
export default [
{
files: ["**/*.ts", "**/*.tsx"]
// any additional configuration for these file types here
}
];
ESLint uses the files
keys from the config file to determine which files should be linted.
--resolve-plugins-relative-to
The --resolve-plugins-relative-to
flag was used to indicate which directory plugin references in your configuration file should be resolved relative to. This was necessary because shareable configs could only resolve plugins that were peer dependencies or dependencies of parent packages.
With flat config, shareable configs can specify their dependencies directly, so this flag is no longer needed.
package.json
Configuration No Longer Supported
With eslintrc, it was possible to use a package.json
file to configure ESLint using the eslintConfig
key.
With flat config, it’s no longer possible to use a package.json
file to configure ESLint. You’ll need to move your configuration into a separate file.
Additional Changes
The following changes have been made from the eslintrc to the flat config file format:
- The
root
option no longer exists. (Flat config files act as ifroot: true
is set.) - The
files
option cannot be a single string anymore, it must be an array. - The
sourceType
option now supports the new value"commonjs"
(.eslintrc
supports it too, but it was never documented).
TypeScript Types for Flat Config Files
You can see the TypeScript types for the flat config file format in the lib/types
source folder on GitHub. The interface for the objects in the config’s array is called Linter.Config
.
You can view the type definitions in lib/types/index.d.ts
.
Visual Studio Code Support
ESLint v9.x support was added in the vscode-eslint
v3.0.10.
In versions of vscode-eslint
prior to v3.0.10, the new configuration system is not enabled by default. To enable support for the new configuration files, edit your .vscode/settings.json
file and add the following:
{
// required in vscode-eslint < v3.0.10 only
"eslint.experimental.useFlatConfig": true
}
In a future version of the ESLint plugin, you will no longer need to enable this manually.