How to Setup ESLint .eslintrc Config

Jump to an article section:

šŸ’” How to Determine the Required ESLint Config

I was reviewing our React + TypeScript project's ESLint config to debug conflicting settings that were causing ESLint to miss errors in large portions of our codebase.

Unfortunately, official documentation, blogs, and StackOverflow answers on ESLint all have varying instructions on how ESLint .eslintrc config should be set up.

There were multiple differing recommendations on which plugins and parsers needed to be declared, or environment values that needed to be added.

After getting our project's ESLint config set up, I discovered the best way to determine the values needed to set up an .eslintrc config is to look at the source code for the eslint-config-* and eslint-plugin-* that you intend to use.

I'll provide an example, but first let's look at the main sections of an .eslintrc config file.

šŸ“ ESLint: An Overview

ESLint is a tool to help you write better JavaScript code.

ESLint does both traditional linting (looking for problematic patterns) and style checking (enforcement of conventions). It catches possible errors and identifies and reports on patterns in your code.

ESLint functions by parsing your code into an Abstract Syntax Tree (AST) data format, and running assertions on the AST of what your code should look or behave like.

ESLint Plugins: Shareable assertions are distributed via eslint-plugin-* NPM packages, and these plugins use the generated AST to create new rules that can be enabled in an .eslintrc.

ESLint Rule Configurations: Shareable base configuration of rules are distributed via eslint-config-* NPM packages.

The Main .eslintrc Properties

Rules

The rules property in .eslintrc allows:

  • Enabling rules that are defined in an eslint-plugin-*
  • Overriding options for rules that were set by an existing eslint-config-* configuration.

ESlint Docs: Configuring Rules

.eslintrc.js
// The example settings below tell ESLint to error when single-quotes are used, and when semi-colons are missing.

module.exports = {
  rules: {
    // enable additional rules
    quotes: ["error", "double"],    semi: ["error", "always"]  }
};

Extends

Instead of manually configuring each rule individually, you can apply a bulk rule configuration from a shared config.

The extends property in .eslintrc allows extending off a set of rule configurations from an existing configuration.

For example, extending off the base ESLint eslint:recommended configuration will enable a subset of core rules that report common problems.

Shared configs are distributed as eslint-config-* NPM packages. One of the commonly used configurations is Airbnb's eslint-config-airbnb.

ESlint Docs: Extending Configuration Files

.eslintrc.js
// The example settings below tell ESLint use the eslint:recommended base configuration and the eslint-config-airbnb shared configuration.

module.exports = {
  extends: ["eslint:recommended"]};

ESLint extends configurations recursively, so a shared eslint-config-* configuration can also have its own extends, env,plugins,parser properties which will apply to the.eslintrc configuration.

Many recommended base configurations shared from eslint-config-* already set the parser, plugins, and env properties.

There is no need to re-declare these properties in your own .eslintrc if you're extending off a recommended base configuration that already has these declared.

Plugins

The plugins property in .eslintrc allows using third-party plugins to apply specific linting rules for different code bases.

For example, eslint-plugin-react, and eslint-plugin-vue, adds specific linting rules for React or Vue projects, respectively.

ESLint plugins contain implementation for additional rules that ESLint will check for, however, these defined ESlint rules still need to be manually configured in an .eslintrc to determine how to handle each rule.

The new rules defined by an eslint-plugin-* still need to be configured under the rules property, or by taking a set of rule configurations in the extends property.

ESlint Docs: Configuring Plugins

.eslintrc.js
// The example settings below tell ESLint use the eslint-plugin-react linting rules, and manually configures two rules.

module.exports = {
  plugins: ["react"],
  parserOptions: {
    ecmaFeatures: {
      jsx: true
    }
  },
  rules: {
    "react/jsx-uses-react": "error",
    "react/jsx-uses-vars": "error"
  }
};

Environment

The env property in .eslintrc declares which environments your code is expected to run in.

Each environment brings with it a certain set of predefined global variables, and prevents ESLint from throwing an no-undef rule error when referencing an expected global variable such as, window, or document.

ESlint Docs: Specifying Environments

.eslintrc.js
// The example settings below tell ESLint to enable browser and node global variables like `window`.

module.exports = {
  env: {
    browser: true,
    node: true
  }
};

Parser

The parser property in .eslintrc declares which parser ESLint should use to parse your code into an AST. By default, ESLint uses Espree as its parser.

ESlint Docs: Specifying Parser

.eslintrc.js
// The example settings below tell ESLint to use the TypeScript ESTree parser.

module.exports = {
  parser: "@typescript-eslint/parser"
};

Parser Options

The parserOptions property in .eslintrc specifies the JavaScript language options you want to support.

By default, ESLint supports ECMAScript 5 syntax. Configure parserOptions to enable support for other ECMAScript versions as well as JSX.

ESlint Docs: Specifying Parser Options

.eslintrc.js
// The example settings below specifies ESLint to enable support for ECMAScript 2018 and JSX.

module.exports = {
  parserOptions: {
    ecmaVersion: 2018,
    ecmaFeatures: {
      jsx: true
    }
  }
};

šŸ‘ØšŸ»ā€šŸ’» Step by Step: How to Configure .eslintrc

The best way to determine the values needed to set up an .eslintrc config is to look at the source code for the eslint-config-* and eslint-plugin-* that you intend to use.

Let's walk through the process to determine what needs to be configured in an .eslintrc.

For this example, let's set up an .eslintrc for a React + TypeScript project.

1) Look at the relevant eslint-plugin-*

Look for the relevant eslint-plugin-* to add additional rules linting rules that ESLint should check for in a React + TypeScript project.

A quick internet search brings us to the NPM packages:

2) Follow the eslint-plugin-* documentation

In the eslint-plugin-* documentation, determine the main config requirements.

The main configs generally involve setting up:

  • plugins: Include the plugin itself
  • parser: Setting a specific parser
  • env: Including any global environment variables

TypeScript

From the @typescript-eslint/eslint-plugin docs, we need a minimum config specifying:

.eslintrc.js
module.exports = {
  parser: "@typescript-eslint/parser", // allows ESLint to understand TypeScript  plugins: ["@typescript-eslint"] // use the plugin rules within ESLint};

React

From the eslint-plugin-react docs, we need a minimum config specifying:

.eslintrc.js
module.exports = {
  plugins: ["react"], // use the plugin rules within ESLint  parserOptions: {
    ecmaFeatures: {
      jsx: true // enable JSX support    }
  },
  settings: {
    react: {
      pragma: "React", // Pragma to use, default to "React"      version: "detect" // React version    }
  }
};

Combined .eslintrc config

.eslintrc.js
module.exports = {
  plugins: [
    "react",
    "@typescript-eslint"
  ],
  parser: "@typescript-eslint/parser",
  parserOptions: {
    ecmaFeatures: {
      jsx: true
    }
  },
  settings: {
    react: {
      pragma: "React",
      version: "detect"
    }
  }
};

It's common for eslint-plugin-* authors to bundle a recommended rule configuration inside a plugin to use as starting a point.

I usually start with these recommended configs, and manually configure any overrides in the rules property later.

TypeScript: plugin:@typescript-eslint/recommended

@typescript-eslint/eslint-plugin comes with a bundled recommended base config.

.eslintrc.js
module.exports = {
  // Remove these config
- parser: "@typescript-eslint/parser",- plugins: ["@typescript-eslint"],
  // Add these config
+ extends: [+   "eslint:recommended", // ESLint's inbuilt "recommended" config+   "plugin:@typescript-eslint/eslint-recommended",+   "plugin:@typescript-eslint/recommended"+ ]};

React: plugin:react/recommended

eslint-plugin-react comes with a bundled recommended base config.

.eslintrc.js
module.exports = {
  // Remove these config
- plugins: ["react"],- parserOptions: {-   ecmaFeatures: {-     jsx: true-   }- },
  // Add these config
+ extends: [+   "eslint:recommended",+   "plugin:react/recommended"+ ],  settings: {
    react: {
      pragma: "React",
      version: "detect"
    }
  }
};

Airbnb ESLint TypeScript Configuration

An alternative to using a recommended base configuration is to use Airbnb's ESLint eslint-config-airbnb config. This provides an all-in-one configuration. It's opinionated, but it sets your project up with general best practices, so you can focus on building your project, and not on configuring hundreds of ESLint rules.

For our TypeScript example, we'll use Matt Turnbull's Airbnb config with TypeScript support: eslint-config-airbnb-typescript.

āœØ Using eslint-config-airbnb-typescript is also nice because the installation docs specifies a very simple minimum setup:

.eslintrc.js
module.exports = {
  extends: ["airbnb-typescript"]};

Note: The common .eslintrc confusion

This is usually the point where configuring .eslintrc becomes confusing.

  • Why is the configuration of eslint-config-airbnb-typescript so simple?
  • Do we still need to specify the parser and plugins?
  • Why could we remove some config, like the parser and plugins, when using the recommended base configs from @typescript-eslint/recommended and react/recommended?

To answer the questions from above, we'll dig into the source code for eslint-config-airbnb-typescript to see what plugins, env, parser, and settings it already declares.

Remember: ESLint extends configurations recursively, so a shared eslint-config-* configuration can also have its own extends, env,plugins,parser properties which will apply to the.eslintrc configuration.

I personally like to go back through my .eslintrc to remove any configs that have already been declared in a shared eslint-config-* that I'm extending off.

plugin:@typescript-eslint/recommended Source Code

If you remember, the @typescript-eslint/eslint-plugin docs required a minimum config with:

.eslintrc.js
module.exports = {
  parser: "@typescript-eslint/parser",
  plugins: ["@typescript-eslint"]
};

If we look in the eslint-config-airbnb-typescript source code you'll see that these two properties are already set on Line 9 - 10.

eslint-config-airbnb-typescript/lib/shared.js - Line 9 - 10
// ... imports

module.exports = {
  plugins: ["@typescript-eslint"],  parser: "@typescript-eslint/parser"
  // ... other configs
};

plugin:react/recommended Source Code

Also for the eslint-plugin-react, it required a minimum config with:

.eslintrc.js
module.exports = {
  plugins: ["react"],  parserOptions: {
    ecmaFeatures: {
      jsx: true    }
  },
  settings: {
    react: {
      pragma: "React",      version: "detect"    }
  }
};

Looking in the eslint-config-airbnb-typescript source code you'll see that it extends off eslint-config-airbnb on Line 3.

eslint-config-airbnb-typescript/index.js
// This file adds some React specific settings. Not using React? Use base.js instead.

module.exports = {
  extends: [
    "eslint-config-airbnb",    "./lib/shared" // TypeScript related config
  ].map(require.resolve),
  
  // ... other configs
};

Following the dependency chain to eslint-config-airbnb, and its source code, you'll see that the required minimum config properties from the eslint-config-airbnb are already set on Line 7 - 15, and Line 511 - 514.

eslint-config-airbnb/rules/react.js - Line 7 - 15, Line 511 - 514
// ... imports and setup

module.exports = {
  plugins: [
    'react',  ],
  parserOptions: {
    ecmaFeatures: {
      jsx: true,    },
  },

  // ... other configs

  settings: {
    react: {
      pragma: 'React',      version: 'detect',    },

    // ... other configs
  }

Use the Force Source, Luke

Enjoy your new ESLint super powers.

Now you'll understand why there are some tutorials and blogs that have certain configs set, while others articles omit those properties.

Just look in the source code šŸ’¾.