Tutorial

How To Customize ESLint Rules with an Ejected Create React App

React

Introduction

Despite whether you like spaces or tabs, semicolons or none, single or double quotes, we can all agree on the importance of consistency and clarity in a codebase. Linting is one approach to establishing consistency and clarity by enforcing coding rules and standards.

Projects created using Create React App have linting with ESLint already working and configured out of the box with sensible defaults. For most scenarios that means that you don’t have anything else to do and everything will just work. The following article should come in handy in cases where you’d want to customize the linting rules.

In this article, you will explore the reasons why we should utilize a linter like ESLint and how to customize it in a Create React App project.

Prerequisites

To complete this tutorial, you’ll need:

This tutorial was verified with Node v16.4.2, npm v7.19.1, react v17.0.2, and eslint v7.11.0.

Understanding Linting

Coding always leaves room for errors, especially with loosely typed languages like JavaScript. By implementing a linter in our code editor and our project, we can save time by discovering errors before we even execute our code.

Most popular code editors either automatically include a linter for the language you’re using or offer multiple extensions so that you can configure how you prefer to lint locally.

While linting locally can and will save us time, there is always the bigger picture - like different code editors and dev environments.

The focus of this article will be on project-level linting with ESLint.

Setting Up the Project

Let’s use Create React App to quickly set up a React project. If you aren’t familiar with it and need a little help getting started, consult Getting Comfortable with Create React App.

First, in the terminal run:

  • npx create-react-app linter-demo

Navigate to the directory:

  • cd linter-demo

Then start the application:

  • npm start

Voila! We’ve now got a project to test.

Ejecting a Create React App Project

Like most things in development, Create React App is practically magic right up until you’re ready to get into some nitty-gritty customizations.

One drawback with the defaults in a project created using Create React App is that you can only configure ESLint by ejecting or forking the project which leaves a lot to be desired for most advanced developers. Sadly you can’t integrate Prettier, change rules to fit your team’s style and you’re locked into the version Create React App deems as the most stable version despite what releases might solve your unique problems. It leaves a lot to be desired in terms of flexibility. There are even complaints and issues that Create React App disables the rules that it suggests following.

To apply new configurations, we will need to eject.

Warning: This article is specifically designed to guide you through the ejecting of the demo project. If you are attempting to eject an existing project, please consult the documentation to ensure this is the desired course of action for your project as it is a one-way operation.

Essentially when we eject, we no longer get updates on our project from the Create React App core. It’s certainly not the end of the world, though, and it allows for more linting customization.

In the terminal run:

  • npm run eject

Confirm the prompt and the script will eject your project.

Among the dependencies added to the project are @babel/core, babel-loader, babel-eslint, eslint,eslint-webpack-plugin, and eslint-plugin-react.

  • eslint is the core JavaScript linter.
  • eslint-loader tells webpack that you want to use eslint in our build
  • babel-loader transpiles our code with webpack
  • babel-eslint provides linting for valid ES6 code
  • eslint-plugin-react extends ESLint rules to cover React

Now you have an ejected Create React App project.

Adding ESLint Rules

ESLint rules can be configured in various files: .eslintrc.js, .eslintrc.cjs, .eslintrc.yaml, .eslintrc.yml, .eslintrc.json. For this demo, we will modify the existing package.json file.

If we don’t quite know where to start, style guides like eslint-config-airbnb allow us to configure our project similar to industry leaders like Airbnb. All that we need to do to implement their style guide is install the corresponding packages. Since their rules are a little strict to start with, we’re going to start with something a little simpler.

Open package.json in your code editor and add rules to eslintConfig:

package.json
{
  "name": "linter-demo",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    // ...
  },
  "scripts": {
    // ...
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ],
    "rules": {
      "no-undef": [ 1 ]
    }
  },
  "browserslist": {
    // ...
  },
  "jest": {
    // ...
  },
  "babel": {
    "presets": [
      "react-app"
    ]
  }
}

This code defines the rule for no-undef (no undefined).

Spotting Errors

If we don’t have a linter set up in our project, it’s difficult to spot errors until the code is compiled.

Can you find the errors we added below?

App.js
import React, { Component } from 'react';
import ReactLogo from './logo.svg';
import './App.css';

class Demo extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p classname="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;
  • We changed our logo’s name in the import but forgot to change it in the src prop.
  • We also changed our class name but forgot to change it in the export.

Without a linter, the problem might be hard to identify. The correct version should look like this:

App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;

With a linter, an error would appear immediately and tip us off that there’s a big problem. Much like how spellcheck gives a squiggly red line when you misspell a word, the linter will give you a squiggly red line when an error is present.

Screenshot of an error tooltip stating: logo is not defined (no-undef).

If we hover over the related squiggle the linter will give us more information on the specific error and even provide a link for more info on that specific rule.

Configuring Specific Rules

Linters like ESLint allow us to create rules for how we want our code to look. These rules include anything from enforcing consistent indentation (indent) to requiring spaces in our curly braces (space-in-brackets).

One of the driving principles of ESLint is that it empowers the developer to decide their own rules and does do not enforce or encourage any specific standards.

All rules can be required, used as a warning, modified or even disabled. In ESLint not only can you completely customize a single rule, but you can disable an entire file, a line, or even just a rule related to a specific line in our code.

We’ve all seen, or even wrote, code that resembles something like the example below:

src/App.js
import React, { Component } from 'react';

import logo from './logo.svg';
import './App.css';

class App extends Component {
      render() {
return (
      <div className="App">

        <header className='App-header'>
          <img src={logo}
          className="App-logo"
          alt="logo" />
                    <h1
                    className="App-title">
            Welcome to React</h1>
        </header>
              <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;

This code is riddled with inconsistency. The wild indentation and spacing make it difficult to read and understand. Imagine if the codebase was large. Digging through the code would seem daunting, frustrating, and might make you want to call in sick.

If we are using linting rules to prevent this, the linter will quickly let us know that this isn’t acceptable.

Earlier we modified package.json file with a single rule. Let’s add some more complex rules to observe what happens to our file:

package.json
{
  // ...
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ],
    "rules": {
      "space-in-parens": [ 0, "always" ],
      "template-curly-spacing": [ 2, "always" ],
      "array-bracket-spacing": [ 2, "always" ],
      "object-curly-spacing": [ 2, "always" ],
      "computed-property-spacing": [ 2, "always" ],
      "no-multiple-empty-lines": [ 2, { "max": 1, "maxEOF": 0, "maxBOF": 0 } ],
      "quotes": [ 1, "single", "avoid-escape" ],
      "no-use-before-define": [ 2, { "functions": false } ],
      "semi": [0, "never"],
      "prefer-const": 1,
      "react/prefer-es6-class": 0,
      "react/jsx-filename-extension": 0,
      "react/jsx-curly-spacing": [ 2, "always" ],
      "react/jsx-indent": [ 2, 4 ],
      "react/prop-types": [ 1 ],
      "react/no-array-index-key": [ 1 ],
      "class-methods-use-this": [ 1 ],
      "no-undef": [ 1 ],
      "no-case-declarations": [ 1 ],
      "no-return-assign": [ 1 ],
      "no-param-reassign": [ 1 ],
      "no-shadow": [ 1 ],
      "camelcase": [ 1 ],
      "no-underscore-dangle" : [0, "always"]
    }
  }
  // ...
}

Save your changes.

Note: Be sure to close and reopen your App.js file or reload your editor window to see the linter changes reflected.

After you reopen the file, it should display the following errors:

Screenshot of previous code example with ESLint rules indicating errors in the code editor.

Take a moment to investigate five of the rules that were applied further:

  • quotes: allows you to define strings in one of three ways: single quotes, double quotes, or backticks.
  • semi: enforce or disallow semicolons
  • react/jsx-curly-spacing: enforce or disallow spaces inside curly braces in JSX props and expressions
  • react/jsx-indent: validates indentions (spaces, tabs)
  • no-undef: disallows undefined variables

For more info on rules and how to modify them, check out eslint and eslint-plugin-react’s rules.

Fixing Multiple Errors

So you have a giant project and didn’t know about ESLint, eh? Now you’re linting and are somewhat terrified with the hundreds or even thousands of errors you’re seeing? Not to fear! ESLint has a feature where you enter the file path and eslint --fix and it automatically fixes every simple error that won’t cause a dumpster fire in our project.

To try this in our current project, run the following from your terminal:

  • ./node_modules/eslint/bin/eslint.js --fix src/App.js

This command reveals that there is one remaining error:

Screenshot of previous code example with ESLint rules indicating errors in the console.

Though we’d love for eslint --fix to be able to fix everything in our file, it has the capability to do some damage. All projects are not created equally so ESLint treads lightly with some fixes.

The last error, class-methods-use-this make a good point, class methods should use this to refer to the current class. We will probably want to change that later but since our project is brand new, we’re just going to ignore it for now.

We could go about ignoring this in three different ways:

  • Disabling the rule across the project
  • Disabling the rule in that file
  • Disabling the rule in the line above the code block

Since this is something that we probably want in other files, we don’t want to disable it across the project and since we want it to be found pretty easily we’re just going to disable it for this specific file.

To disable the rule in a file, add the following comment to the first line:

src/App.js
/* eslint-disable class-methods-use-this */

Rerun the command and it will ignore this rule.

Conclusion

In this article, you explored the reasons why we should utilize a linter like ESLint and how to customize it in a Create React App project.

We brainstormed with a few developers on how ESLint has saved them time and here’s a short list of what we came up with:

  • Stop the build if any errors are detected.
  • Reinforce good habits. Did you use var where you could have used let or const?
  • Help identify unused or unnecessary code, resulting in a slimmed-down package.
  • Remind you to use object destructing.
  • Prevent undefined variables.
  • Potentially identify missing files or packages.

Now that we have customized ESLint rules for our project, we never need to have the tabs versus spaces debate again!

Also, this article discusses customizing linting rules by ejecting from Create React App, but there’s also an alternative solution using react-app-rewired and react-app-rewire-eslint.

Creative Commons License