ESLint, Don't Write JavaScript Without It!

by R. Mark Volkmann 

January 2017


ESLint is, as the website describes, the "pluggable linting utility for JavaScript and JSX." Linters reports many syntax errors and potential run-time errors. ESLint also reports deviations from specified coding guidelines. Error messages identify the violated rules, making it easy to adjust their configuration if you disagree with the defaults.

Languages that require compilers have a way to give feedback to developers before code is executed. Since JavaScript doesn't require a compiler, linters are needed to play that role.

Alternatives to ESLint exist, such as JSLint and JSHint, but none compare in terms of the number of rules supported, ability to configure the rules, and ability to add custom rules.

While you can write JavaScript code without using a linter, that's a bit like playing football without a helmet or taking food out of an oven without mitts. Somebody is going to get hurt.

Common Complaints

Some say that making all the changes suggested by ESLint takes too much time. Fortunately, ESLint supports a "fix" mode that automates making many of the changes. There are several ways to accomplish this. Running eslint --fix file-path from a terminal/command-line modifies a file in-place. Editor/IDE plugins allow this to be done with a single keyboard shortcut. Version control systems like Git can run this on checkout and checkin of files.

Often developer teams cannot agree on the rules to be enforced. One solution is for each developer to have their own ESLint configuration file and run ESLint in fix mode on files as they are checked out. The team can have an official project ESLint configuration files and run ESLint in fix mode on files as they are checked in. Everybody wins!


To install ESLint globally:

npm install -g eslint

npm is installed automatically when Node.js is installed. For details on npm, see

To install ESLint locally within a project:

npm install --save-dev eslint


No rules are enforced by default. Desired rules must be configured. A list of built-in rules and documentation on them can be found at


There are several ways to configure ESLint including:

Use of a file named simply .eslintrc containing JSON or YAML is deprecated.

ESLint searches upward from current directory for these files. It combines settings in all of the configuration files found with settings in the closest files taking precedence. A configuration file in the home directory is only used if no other configuration files are found.

Rule configuration files can specify values for these properties:

env This is a list of environments in which your code can run. Each environment adds globals (described next). A list of supported environments can be found at Common values include browseres6nodejasminejest, and mocha.
globals This is a list of acceptable global variables and whether they can be modified (modification isn't typically allowed).
parser By default, ESLint uses the Espree parser to parse the code it checks. This property can specify a different parser. If using newer JS features, Flow type annotations, or JSX, run "npm install -g babel-eslint" and change this property to "babel-eslint". Another compatible parser is Esprima. Work is underway to create an ESLint-compatible TypeScript parser. See
parserOptions This specifies the ECMA version, source type ("module" or "script"; see next section), and extra features (such as JSX).
plugins This specifies a set of plugins to use. Plugins supplement the rules and environments provided by ESLint.
rules This specifies the rules to be enforced (or not) and their configuration. It will almost certainly be the largest part of the configuration.
extends This is a string or array of strings that refer to other configuration files. The values are either relative file references or names of installed npm packages that contain an ESLint configuration file. Popular examples include "airbnb""google", and "standard". The npm package names for these have the prefix "eslint-config-". Rule settings in the current file override settings for the same rules in configurations being extended. All the built-in rules that are recommended by the ESLint team can be enabled by extending "eslint:recommended". These rules have a check mark in front of them on the rules page.

Scripts vs. Modules

Each JavaScript file in an application is treated either as a script or a module. The differences are described in the following table:

 Scripts  Modules  
Default Mode  non-strict    strict
Top-level Variables  global    local to module 
Top-level value of this  window    undefined
Can use import statements    no    yes

Typically, most .js files should be treated as modules.


ESLint plugins provide additional rules and environments. A list of available plugins can be found at

Popular ESLint plugins include:

Configuration File Creation

To create an initial configuration file, run eslint --init. This asks a series of yes/no and multiple choice questions about your preferences. Alternatively, it can inspect a set of files you specify to infer your preferences. After this configuration file is created, it can be customized.

Here is an example session using rules from an existing style guide:

eslint-plugin-volkmann$ eslint --init
? How would you like to configure ESLint? Use a popular style guide
? Which style guide do you want to follow? Google
? What format do you want your config file to be in? JSON
Successfully created .eslintrc.json file in /Users/Mark/Documents/programming/languages/javascript/eslint/eslint-plugin-volkmann

Here is an example session based on answers to questions:

eslint-plugin-volkmann$ eslint --init
? How would you like to configure ESLint? Answer questions about your style
? Are you using ECMAScript 6 features? Yes
? Are you using ES6 modules? Yes
? Where will your code run? Browser
? Do you use CommonJS? No
? Do you use JSX? Yes
? Do you use React Yes
? What style of indentation do you use? Spaces
? What quotes do you use for strings? Single
? What line endings do you use? Unix
? Do you require semicolons? Yes
? What format do you want your config file to be in? JSON
Successfully created .eslintrc.json file in /Users/Mark/Documents/programming/languages/javascript/eslint/eslint-plugin-volkmann

JSON Configuration

Here's an example of a JSON-based configuration file that would typically be in a file named .eslintrc.json.

  1. {
  2. "env": {
  3. "browser": true,
  4. "es6": true,
  5. "jest": true,
  6. "node": true
  7. },
  9. "globals": {
  10. "$": false
  11. },
  13. "parser": "babel-eslint",
  15. "parserOptions": {
  16. "ecmaVersion": 7,
  17. "sourceType": "module",
  18. "ecmaFeatures": {
  19. "jsx": true
  20. }
  21. },
  22. "plugins": [
  23. "html",
  24. "react"
  25. ],
  27. "rules": {
  28. "brace-style": ["error", "1tbs"], // stands for "one true brace style"
  29. ... many more rules go here ...
  30. }
  31. }

The syntax for specifying rules is:

"name": setting or
"name": [setting, config]
where setting is one of
"off" or 0
"warn" or 1
"error" or 2
and config is rule-specific.

For a comprehensive set of configured rules, consider my configuration file at Feel free to use this file as a starting point for your customizations.

Running ESLint

To lint a specific file: eslint file-path.

To lint all JavaScript files in and below the current directory: eslint **/*.js.

To lint all files in and below the current directory: eslint **. Typically, it is desirable to only run ESLint on .js files.

To run ESLint using an npm script, add the following in the scripts section of package.json:

"eslint": "eslint **/*.js"

This can be run by entering npm run eslint

To lint JavaScript in HTML files:

To ignore certain files, list the directories and files to ignore in the file .eslintignore in the project root directory.

For more details on configuring ESLint, see

Command-line Options

To specify a configuration file to use rather than automatically search for one:
eslint -c config-file-path files-to-check

To run on files with an extension other than .js:
eslint --ext extension files-to-check
A common example is the jsx extension.

To fix all fixable errors:
eslint --fix files-to-check-and-fix
This fixes many, but not all, rule violations in source files.

To skip fixes for a certain rule:
eslint --fix --rule 'some-rule: off' some-file.js
To skip fixes for multiple rules, specify the --rule option multiple times.

To cache checked files so they are only rechecked if they change:
eslint --cache
This creates the file .eslintcache. Add --debug to verify that caching is working. Look for output lines containing "Linting".

For information on more command-line options, enter:
eslint --help

Output Formatters

The ESLint output format can be specified with the command-line option -f {formatter-name}. There is no option for specifying this in an ESLint configuration file. There are many formatting options to choose from. Some formatters produce output suitable for feeding into other tools. For details, see

Recommended formatters include stylish (the default) and codeframe.

Here is an example of stylish output that describes errors for a custom rule that we'll build later in this article:

  1. /Users/Mark/Documents/programming/languages/javascript/eslint/eslint-plugin-volkmann/bad-code.js
  2. 2:17 error prefer x**2 over multiplication volkmann/exponentiation-operator
  3. 3:15 error prefer x**3 over multiplication volkmann/exponentiation-operator
  4. 7:18 error prefer x**2 volkmann/exponentiation-operator
  5. 8:16 error prefer x**3 volkmann/exponentiation-operator
  6. 12:17 error prefer (x * 2)**(3 + 4) volkmann/exponentiation-operator
  8. ✖ 5 problems (5 errors, 0 warnings)

Here is an example of the same errors using the codeframe formatter:

  1. error: prefer x**2 over multiplication (volkmann/exponentiation-operator) at bad-code.js:2:17:
  2. 1 | const x = 3;
  3. > 2 | const squared = x * x;
  4. | ^
  5. 3 | const cubed = x * x * x;
  6. 4 | console.log('squared =', squared);
  7. 5 | console.log('cubed =', cubed);
  10. error: prefer x**3 over multiplication (volkmann/exponentiation-operator) at bad-code.js:3:15:
  11. 1 | const x = 3;
  12. 2 | const squared = x * x;
  13. > 3 | const cubed = x * x * x;
  14. | ^
  15. 4 | console.log('squared =', squared);
  16. 5 | console.log('cubed =', cubed);
  17. 6 |
  20. error: prefer x**2 (volkmann/exponentiation-operator) at bad-code.js:7:18:
  21. 5 | console.log('cubed =', cubed);
  22. 6 |
  23. > 7 | const squared2 = Math.pow(x, 2);
  24. | ^
  25. 8 | const cubed2 = Math.pow(x, 3);
  26. 9 | console.log('squared2 =', squared2);
  27. 10 | console.log('cubed2 =', cubed2);
  30. error: prefer x**3 (volkmann/exponentiation-operator) at bad-code.js:8:16:
  31. 6 |
  32. 7 | const squared2 = Math.pow(x, 2);
  33. > 8 | const cubed2 = Math.pow(x, 3);
  34. | ^
  35. 9 | console.log('squared2 =', squared2);
  36. 10 | console.log('cubed2 =', cubed2);
  37. 11 |
  40. error: prefer (x * 2)**(3 + 4) (volkmann/exponentiation-operator) at bad-code.js:12:17:
  41. 10 | console.log('cubed2 =', cubed2);
  42. 11 |
  43. > 12 | const complex = Math.pow(x * 2, 3 + 4);
  44. | ^
  45. 13 | console.log('complex =', complex);
  46. 14 |
  49. 5 errors found.

Integrating with IDEs and Editors

Many integrations are supported. These are summarized at

For Vim, install the Syntastic plugin at and add the following line to .vimrc: let g:syntastic_javascript_checkers=['eslint']

For Emacs, use the Flycheck syntax checking extension at

For Atom, see or

For Code, see

For WebStorm/IDEA, see

Overriding Rules in Source Files

To disable all rules in the current scope until re-enabled:

/* eslint-disable */
/* eslint-enable */

To disable specific rules in the current scope until re-enabled:

/* eslint-disable rule1, rule2, ... */
/* eslint-enable rule1, rule2, ... */

To disable all rules for a single line:

code-line // eslint-disable-line


// eslint-disable-next-line

To disable specific rules for a single line:

code-line // eslint-disable-line rule1, rule2, ...


// eslint-disable-next-line rule1, rule2, ...


Specifying Globals in Source Files

In addition to listing global variables in an ESLint configuration file, these can be specified in source files that use them.

To allow use of read-only global variables in the current scope:

/* global name1, name2 */

To allow use of modifiable global variables in the current scope:

/* global name1: true, name2: true */

The default is false meaning read-only.

Include these comments at top of a file for file scope.

Checking for Unused Rules

New versions of ESLint and its plugins often add support for new rules. These must be specified in an ESLint configuration file to take advantage of them. The npm package eslint-find-rules finds supported rules in ESLint and plugins that are not specified in the current ESLint configuration file.

This must run from npm. Follow these steps:

  1. Select a project to use for performing this check.
  2. cd to the root directory of the project.
  3. npm install --save-dev eslint eslint-find-rules eslint-plugin-*
    where eslint-plugin-* represents each of the optional plugins being used.
  4. Add the following script to package.json:
    "efr": "eslint-find-rules --unused ~/.eslintrc.json --no-error"
    This can point to a shared ESLint configuration file such as ~/.eslintrc.json or one that is project-specific.
  5. npm run efr
    When adding configuration for new rules, it's easy to add syntax errors and invalid rule configurations. This command will output a stacktrace describing such errors.

Custom ESLint Plugins

Adding custom rules to ESLint is easier than you probably imagine. To demonstrate this, we'll build a plugin that contains one rule. The rule looks for code that could use the ES2016 exponentiation operator. The following table shows examples of code that it would flag and the fix it would apply.

Offending Code  Fixed Code 
x * x  x**2
x * x * x  x**3
x * x * x * y * y  x**3 * y**2
Math.pow(x, 2)  x**2
Math.pow(x * y, a + b)   (x * y)**(a + b)


The steps to implement, test, package, deploy, install, and use custom rules are:

1. Write code that violates the rules to be created. Here's a good starting point:

  1. const x = 3;
  2. const squared = x * x;
  3. const cubed = x * x * x;
  4. console.log('squared =', squared);
  5. console.log('cubed =', cubed);
  7. const squared2 = Math.pow(x, 2);
  8. const cubed2 = Math.pow(x, 3);
  9. console.log('squared2 =', squared2);
  10. console.log('cubed2 =', cubed2);
  12. const complex = Math.pow(x * 2, 3 + 4);
  13. console.log('complex =', complex);

2. Use AST Explorer to see the abstract syntax tree for this code. We'll discuss AST Explorermore later.

3. Implement the rules.

4. Test the rules.

5. Package the rules as a plugin by placing them in a Github repository whose name matches "eslint-plugin-name".

6. Deploy the plugin by entering npm publish. Each time this is done, the version in package.json must be bumped. This command requires annpm account that can be created by running these commands:

npm set "Your Name" npm set "your-email-address" npm set "your-blog-or-home-page-url" npm adduser

7. Install the plugin by entering npm install -g eslint-plugin-name for global use or without the -g option to install in a specific project.

8. Configure use of the rules by modifying an ESLint configuration file, the global one or one that is project-specific.

Custom rules can be tested and used without packaging, deploying, and installing. The steps to do this are described later.

Plugin File Structure

The recommended file structure for a plugin is:


Plugin index.js

This file uses require to pull in all the rule definition files in the plugin and provides a default configuration for them. An example follows:

  1. module.exports = {
  2. rules: {
  3. /* eslint-disable global-require */
  4. 'exponentiation-operator': require('./lib/rules/exponentiation-operator')
  5. },
  6. configs: {
  7. all: {
  8. parserOptions: {
  9. ecmaVersion: 7
  10. },
  11. rules: {
  12. 'volkmann/exponentiation-operator': 'error'
  13. }
  14. }
  15. }
  16. };

AST Explorer

This is a free, web-based tool for viewing the abstract syntax tree of a snippet of JavaScript code. To use it, browse, enter code in the left pane, and see the corresponding AST in the right pane. Select code in the left pane to cause the corresponding part of the AST to be highlighted in the right pane.

Rule Code

The content of the file lib/rules/exponentiation-operator.js is provided below.

The create method returns an object with methods that will be invoked using the visitor pattern. When AST nodes are encountered, the corresponding method, if one is defined, is invoked. In this case the two methods are BinaryExpression and CallExpression. When a nested BinaryExpression is found, this code processes all its descendants. Later this method will be called again for nested expressions that have already been processed, so we need to skip those. ESLint doesn't allow modifying AST nodes, so we can't just add a "processed" flag on them. This is why we use the array processedStarts.

  1. /**
  2.  * This looks for expressions like "x * x * x"
  3.  * and replaces them with "x**3".
  4.  * The example above is parsed as a BinaryExpression times x.
  5.  * The BinaryExpression is parsed as x * x.
  6.  * So x * x * x would be parsed as (x * x) * x.
  7.  * This also replaces expressions like "Math.pow(x, 3)"
  8.  * with "x**3".
  9.  * @fileoverview Prefer exponentiation operator over multiplication and Math.pow.
  10.  * @author R. Mark Volkmann
  11.  */
  12. module.exports = {
  13. meta: {
  14. docs: {
  15. description: 'prefer ** over multiplication',
  16. category: 'Stylistic Issues',
  17. recommended: true
  18. },
  19. fixable: 'code',
  20. schema: [ // This is a JSON schema format description of a rule’s options.
  21. // Include this option for "x ** 2". Exclude for "x**2".
  22. {enum: ['spaceAround']}
  23. ]
  24. },
  25. create(context) {
  26. const processedStarts = [];
  27. return {
  28. BinaryExpression(node) {
  29. // This code only cares about multiplication operators.
  30. if (node.operator !== '*') return;
  32. let exponent = 2; // We know it is at least this.
  34. // BinaryExpressions have left and right operands.
  35. let {left, right} = node;
  37. // This rule only handles BinaryExpressions where
  38. // the right operand is an identifier.
  39. if (right.type !== 'Identifier') return;
  41. // Avoid processing nested BinaryExpressions multiple times.
  42. if (processedStarts.includes(right.start)) return;
  44. const {name} = right;
  46. // Increase exponent for each additional * BinaryExpression
  47. // that uses the same operand name.
  48. while (left.type === 'BinaryExpression') {
  49. // If we find an operator other than *, this rule cannot be applied.
  50. if (left.operator !== '*') return;
  52. // Switch to the nested operands.
  53. right = left.right;
  54. left = left.left;
  56. // All the identifier names must match.
  57. if ( !== name) return;
  59. // Keep track of BinaryExpressions that have already been processed.
  60. processedStarts.push(right.start);
  62. exponent++;
  63. }
  65. // This rule only handles cases where the most deeply nested
  66. // BinaryExpression has a left operator that is an Identifier.
  67. if (left.type === 'Identifier' && === name) {
  68. // Process spaceAround option.
  69. const space = context.options.includes('spaceAround') ? ' ' : '';
  71. // Report the rule violation.
  72. const text = + space + '**' + space + exponent;
  74. node,
  75. message: 'prefer ' + text + ' over multiplication',
  76. // If running in --fix mode, apply this fix.
  77. fix(fixer) {
  78. // Warning: When the spaceAround option is not used,
  79. // the fix for the built-in space-infix-ops rule
  80. // will add spaces back around **.
  82. // Only one fixer method can be called
  83. // and its result must be returned.
  84. return fixer.replaceText(node, text);
  85. }
  86. });
  87. }
  88. }, // end of BinaryExpression method
  90. CallExpression(node) {
  91. const {callee} = node;
  92. if ( !== 'Math') return;
  93. if ( !== 'pow') return;
  95. function getCode(arg) {
  96. const sourceCode = context.getSourceCode();
  97. const needParens = arg.type === 'BinaryExpression';
  98. const code = sourceCode.getText(arg);
  99. return needParens ? '(' + code + ')' : code;
  100. }
  102. const [base, exponent] = node.arguments;
  103. const baseCode = getCode(base);
  104. const exponentCode = getCode(exponent);
  105. const text = baseCode + '**' + exponentCode;
  108. node,
  109. message: 'prefer ' + text,
  110. fix(fixer) {
  111. return fixer.replaceText(node, text);
  112. }
  113. });
  114. }
  115. };
  116. }
  117. };

Code Fixes

In addition to flagging rule violations, ESLint rules can fix the offending code. To do this, the rule must include a fix method in the object passed to (as seen at line 77 of the example rule code above). This method is passed a fixer which is a RuleFixer object defined in eslint/lib/util/rule-fixer.js. Methods on the fixer object are described at

In the fixer method descriptions below, a "token" refers to a piece of text that is actually in the code, whereas a "node" refers to an abstract representation of what the code means (credit goes to Teddy Katz for this explanation). A range is an array containing start and end indexes. AST nodes have a range property with this kind of value.

Fixer MethodDescription
insertTextAfter(nodeOrToken, text)  inserts text after the given node or token
insertTextAfterRange(range, text)  inserts text after the given range
insertTextBefore(nodeOrToken, text)   inserts text before the given node or token
insertTextBeforeRange(range, text)  inserts text before the given range
remove(nodeOrToken)  removes the given node or token
removeRange(range)  removes text in the given range
replaceText(nodeOrToken, text)  replaces the text in the given node or token
replaceTextRange(range, text)  replaces the text in the given range

For more detail on implementing custom rules than is presented here, see

Rule Tests

Tests for rules are placed in the tests/lib/rules directory in files with the same names as the files that implement the rules.

ESLint uses Mocha for testing rules. The steps to configure this are:

  1. npm install --save-dev mocha
  2. Add the following line in the scripts section of package.json:
    "test": "mocha tests/lib/rules"

Here's the test file for our rule, in tests/lib/rules/exponentiation-operator.js.

  1. /**
  2.  * @fileoverview Tests for exponentiation operator
  3.  * @author R. Mark Volkmann
  4.  */
  5. const RuleTester = require('eslint').RuleTester;
  6. const ruleName = 'exponentiation-operator';
  7. const rule = require('../../../lib/rules/' + ruleName);
  9. const ruleTester = new RuleTester();
  11., rule, {
  12. valid: [
  13. {code: 'x**2', parserOptions: {ecmaVersion: 7}},
  14. {code: 'x**3', parserOptions: {ecmaVersion: 7}}
  15. ],
  16. invalid: [
  17. {
  18. code: 'x * x',
  19. errors: [
  20. {
  21. message: 'prefer x**2 over multiplication',
  22. type: 'BinaryExpression'
  23. }
  24. ],
  25. output: 'x**2'
  26. },
  27. {
  28. code: 'x * x * x',
  29. options: ['spaceAround'],
  30. errors: [
  31. {
  32. message: 'prefer x ** 3 over multiplication',
  33. type: 'BinaryExpression'
  34. }
  35. ],
  36. output: 'x ** 3'
  37. },
  38. {
  39. code: 'x * x * x * x',
  40. errors: [
  41. {
  42. message: 'prefer x**4 over multiplication',
  43. type: 'BinaryExpression'
  44. }
  45. ],
  46. output: 'x**4'
  47. },
  48. {
  49. code: 'x * x * y',
  50. errors: [
  51. {
  52. message: 'prefer x**2 over multiplication',
  53. type: 'BinaryExpression'
  54. }
  55. ],
  56. output: 'x**2 * y'
  57. },
  58. {
  59. code: 'Math.pow(x, 2)',
  60. errors: [
  61. {
  62. message: 'prefer x**2',
  63. type: 'CallExpression'
  64. }
  65. ],
  66. output: 'x**2'
  67. },
  68. {
  69. code: 'Math.pow(x * y, a + b)',
  70. errors: [
  71. {
  72. message: 'prefer (x * y)**(a + b)',
  73. type: 'CallExpression'
  74. }
  75. ],
  76. output: '(x * y)**(a + b)'
  77. },
  78. ] // end of invalid
  79. });

To run the tests: npm test

Using Custom Rules Without Deploying

Custom rules don't need to be deployed to npm and installed in order to use them. This is useful for rules that are project-specific, rules that aren't intended to be deployed to npm, or for testing rules that haven't been deployed yet.

The steps to achieve this are:

  1. Place rule implementation files in a common directory.
  2. Add configuration for the custom rules to the ESLint configuration file that will be used.
  3. Run eslint with the --rulesdir option set to the directory from step 1.

For example, to use our "exponentiation-operator" rule from the directory containing the lib directory which contains rules/exponentiation-operator.js, we could add the following line to .eslintrc.json:

"exponentiation-operator": ["error", "spaceAround"]

Then we can run ESLint with eslint --rulesdir lib/rules some-filename.js 

Running Rule Tests

To run the tests, enter npm test or just npm t.

Here's the output from our test.

> mocha tests/lib/rules
      ✓ x**2 (47ms)
      ✓ x**3
      ✓ x * x
      ✓ x * x * x
      ✓ x * x * x * x
      ✓ x * x * y
      ✓ Math.pow(x, 2)
      ✓ Math.pow(x * y, a + b)
  8 passing (75ms)

Configuring Files to be Checked

Files under node_modules are not checked. This is good because it's likely that JavaScript files found there will violate many of the rules you have configured.

Files in other locations can be ignored by listing either the files or the directories that contain them in the .eslintignore file. The command line options --ignore-path and --ignore-pattern can also be used to exclude files. Warning messages listing the ignored files will be output. Include the --quiet option to suppress these.


I hope this article has provided you with everything you need to know to use ESLint. You should also feel ready to begin implementing your own ESLint rules. Please feel free to send questions and feedback to

Thank you very much to Charles Sharp for reviewing this article! He makes everything I write better. Also, thanks to Kent C. Dodds for making me aware of AST Explorer.


WebSanity Top Secret