A mostly reasonable approach to JavaScript and React using Airbnb's ESLint config, updated for ESLint 9+
npm install eslint-flat-config-airbnbA mostly reasonable approach to JavaScript and React using Airbnb's ESLint config, updated for ESLint 9+
This fork of the Airbnb JavaScript Style Guide updates the rules and configurations to support ESLint 9.0+ while maintaining the core principles of clean, consistent, and modern JavaScript code.
```
npm install --save-dev eslint-flat-config-airbnb
We export multiple ESLint configurations for your usage. Extend it in your eslint.config.mjs (or .js, .cjs, .ts, .mts, *cts).
Our default export contains all of our ESLint and React rules. It requires eslint.
`
// eslint.config.mjs
import globals from 'globals';
import tseslint from 'typescript-eslint';
import airbnb from 'eslint-flat-config-airbnb';
import stylistic from '@stylistic/eslint-plugin';
export default [
{
ignores: ['node_modules', 'build', 'dist'],
},
{
files: ['src/*/.{js,mjs,cjs,ts}'],
},
...tseslint.configs.recommended,
...airbnb,
{
languageOptions: {
globals: globals.node,
},
plugins: {
'@stylistic': stylistic,
},
settings: {
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
},
{
rules: {
'no-underscore-dangle': ['error', { allow: ['_id', '__dirname', '__type'] }],
'@stylistic/template-curly-spacing': ['error', 'always'],
'@stylistic/object-curly-spacing': ['error', 'always'],
'@stylistic/computed-property-spacing': ['error', 'always'],
'no-use-before-define': ['error', { functions: false }],
'prefer-const': 1,
complexity: ['error', { max: 15 }],
},
},
];
`
Our base export contains all ESLint rules, including ECMAScript6+.base also exports these configs - bestPractices, errors, es6, node, strict, style, imports, variables which are javasScript objects.
`
// eslint.config.mjs
import globals from 'globals';
import { base } from 'eslint-flat-config-airbnb';
import stylistic from '@stylistic/eslint-plugin';
export default [
{
ignores: ['node_modules', 'build', 'dist'],
},
...base,
{
languageOptions: {
globals: globals.node,
},
plugins: {
'@stylistic': stylistic,
},
rules: {
'no-underscore-dangle': ['error', { allow: ['_id', '__dirname', '__type'] }],
'@stylistic/template-curly-spacing': ['error', 'always'],
}
},
];
`
#### importing sub-rules from base
`
// eslint.config.mjs
import globals from 'globals';
import { style } from 'eslint-flat-config-airbnb/base';
import stylistic from '@stylistic/eslint-plugin';
export default [
{
ignores: ['node_modules', 'build', 'dist'],
},
style,
{
languageOptions: {
globals: globals.node,
},
plugins: {
'@stylistic': stylistic,
},
rules: {
'no-underscore-dangle': ['error', { allow: ['_id', '__dirname', '__type'] }],
'@stylistic/template-curly-spacing': ['error', 'always'],
}
},
];
`
Our react export contains all React rules.react also exports these configs - reactA11y, reactHooks, reactCore which are javasScript objects.
`
// eslint.config.mjs
import globals from 'globals';
import { react } from 'eslint-flat-config-airbnb';
import stylistic from '@stylistic/eslint-plugin';
export default [
{
ignores: ['node_modules', 'build', 'dist'],
},
...react,
{
languageOptions: {
globals: globals.node,
},
plugins: {
'@stylistic': stylistic,
},
rules: {
'no-underscore-dangle': ['error', { allow: ['_id', '__dirname', '__type'] }],
'@stylistic/template-curly-spacing': ['error', 'always'],
}
},
];
`
#### importing sub-rules from react
`
// eslint.config.mjs
import globals from 'globals';
import { reactCore } from 'eslint-flat-config-airbnb/react';
import stylistic from '@stylistic/eslint-plugin';
export default [
{
ignores: ['node_modules', 'build', 'dist'],
},
reactCore,
{
languageOptions: {
globals: globals.node,
},
plugins: {
'@stylistic': stylistic,
},
rules: {
'no-underscore-dangle': ['error', { allow: ['_id', '__dirname', '__type'] }],
'@stylistic/template-curly-spacing': ['error', 'always'],
}
},
];
`
#### Custom eslint.config.mjs used across nodejs repo's
`
import globals from 'globals';
import { base } from 'eslint-flat-config-airbnb';
import stylistic from '@stylistic/eslint-plugin';
export default [
{
ignores: ['node_modules', 'build', 'dist'],
},
{
files: ['src/*/.{js,mjs,cjs,ts}'],
},
...base,
{
languageOptions: {
globals: globals.node,
},
plugins: {
'@stylistic': stylistic,
},
settings: {
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
},
{
rules: {
'import/no-extraneous-dependencies': ['error'],
'no-underscore-dangle': ['error', { allow: ['_id', '__dirname', '__type'] }],
'@stylistic/indent': ['error', 4],
'@stylistic/template-curly-spacing': ['error', 'always'],
'@stylistic/computed-property-spacing': ['error', 'always'],
'no-use-before-define': ['error', { functions: false }],
'prefer-const': 1,
'@stylistic/max-len': ['error', { code: 200 }],
complexity: ['error', { max: 15 }],
},
},
{
files: ['eslint.config.mjs'],
rules: {
'import/no-extraneous-dependencies': 'off',
},
},
];
`
Q1: Can you use flat config for projects using commonjs (i.e., using `module.exports`)?`
Ans: Yes. Change file format to .mjs` and use ES6 import and export statements in `eslint.config.mjs`.
Q2: Getting warning: `Warning: React version was set to "detect" in eslint-plugin-react settings, but the "react" package is not installed. Assuming latest React version for linting.``
Ans: If you are not using react package in your project, you can use base` rules instead as described in above section.
Q3: Getting error: `ReferenceError: stylistic is not defined` or for any other eslint plugin instead of `stylistic`.`
Ans: It is likely because there are some rule(s) defined in rules` object in `eslint.config.mjs` and its corresponding package is not imported and added in `plugins` separately in the file even though it is installed with `eslint-flat-config-airbnb` package.
Q4: Getting error: `ConfigError: Config (unnamed): Key "plugins": Cannot redefine plugin "import".` or for any other eslint plugin instead of `import`.`
Ans: Flat config doesn't support multiple declaration of plugins` keyword. If there are mutiple packages which are importing with `plugins` keyword, remove them until atmost one declaration is present in config namespace.
`
// eslint.config.mjs
import globals from 'globals';
import { base } from 'eslint-flat-config-airbnb';
import stylistic from '@stylistic/eslint-plugin';
import importPlugin from 'eslint-plugin-import';
// eslint-disable-next-line no-unused-vars
const { plugins, ...importConfigWithoutPlugin } = importPlugin.flatConfigs.recommended;
export default [
{
ignores: ['node_modules', 'build', 'dist'],
},
...base,
importConfigWithoutPlugin,
{
languageOptions: {
globals: globals.node,
},
plugins: {
'@stylistic': stylistic,
},
rules: {
'no-underscore-dangle': ['error', { allow: ['_id', '__dirname', '__type'] }],
'@stylistic/template-curly-spacing': ['error', 'always'],
}
},
];
`
OR
`
// eslint.config.mjs
/ eslint-disable no-unused-vars /
import globals from 'globals';
import { bestPractices, errors, es6, node, strict, style, imports, variables } from 'eslint-flat-config-airbnb/base';
import stylistic from '@stylistic/eslint-plugin';
import importPlugin from 'eslint-plugin-import';
import nodePlugin from 'eslint-plugin-n';
const { plugins, ...importConfigWithoutPlugin } = importPlugin.flatConfigs.recommended;
const { plugins: bestPracticesPlugins, ...bestPracticesConfigWithoutPlugin } = bestPractices;
const { plugins: errorsPlugins, ...errorsConfigWithoutPlugin } = errors;
const { plugins: es6Plugins, ...es6ConfigWithoutPlugin } = es6;
const { plugins: nodePlugins, ...nodeConfigWithoutPlugin } = node;
const { plugins: stylePlugins, ...styleConfigWithoutPlugin } = style;
const { plugins: importsPlugins, ...importsConfigWithoutPlugin } = imports;
export default [
{
ignores: ['node_modules', 'build', 'dist'],
},
importConfigWithoutPlugin,
bestPracticesConfigWithoutPlugin,
errorsConfigWithoutPlugin,
es6ConfigWithoutPlugin,
nodeConfigWithoutPlugin,
styleConfigWithoutPlugin,
importsConfigWithoutPlugin,
strict,
variables,
{
languageOptions: {
parserOptions: { // set correct parser
ecmaVersion: 'latest',
sourceType: 'module',
},
},
},
{
languageOptions: {
globals: globals.node,
},
plugins: { // re-import plugins separately
'@stylistic': stylistic, // for bestPracticesConfigWithoutPlugin, errorsConfigWithoutPlugin, es6ConfigWithoutPlugin, styleConfigWithoutPlugin
import: importPlugin, // for importConfigWithoutPlugin, importsConfigWithoutPlugin
n: nodePlugin, // nodeConfigWithoutPlugin
},
rules: {
'no-underscore-dangle': ['error', { allow: ['_id', '__dirname', '__type'] }],
}
},
];
``