Configuring ESLint with Prettier

Last update: 05 August, 2020
Table of contents
Update: I suggest to first check the Prettier documentation about Integrating with linters. That information was not available when I started writing this post, and I think they did a good job explaining the different options.

In this post, we’ll see how we can configure 2 really powerful tools we can use for linting and formatting our code. The first one is ESLint which is a JavaScript linter. ESLint can analyze our code and point out syntactic errors and possible bugs from low-quality code. ESLint in addition to that can be used as a code formatter. It does that by enforcing the formatting rules we write ourselves or those of the style guide we’re using. For example, Airbnb’s style guide has a few formatting rules. Prettier on the other hand is just a code formatter. It makes our code look pretty by breaking long statements into multiple lines, removing extra spaces and more. Prettier can’t help us with syntactic errors but it’s a much more powerful code formatter compared to ESLint.

Possible problems

As we see, in order to write high-quality code we want to use both of these tools. So where’s the problem? We’ll just install them and follow the official documentation. Pretty easy right? Although this is a reasonable request in theory, in practice you may experience some problems.

  • The first problem is that Prettier is a highly opinionated tool. This means that Prettier formatting rules are very specific and not widely adopted by everyone. In addition to that, it doesn’t have a lot of flexibility with the configuration options it provides. As a result, it’s quite common for these tools to come into conflict. For example, you may run a script to format a file with Prettier and then immediately you’ll see the ESLint plugin reporting an error due to the formatting you just applied.
  • Another problem is that in order to use these tools you need a ridiculous amount of packages and editor plugins. Not only that, you have more than one way to configure them depending on your preferences. As a result, it’s quite common to end up with more dev dependencies or editor plugins that you actually need.

In this post, we’ll focus on configuring these 2 tools in a way that seems the most reasonable to me. We want to first format our code with Prettier and then ask ESLint to find linting errors and fix them. This way we’ll end up with good looking code and in addition to that, we’ll remain true to our coding style. But we’ll also briefly explore some other options we have in the end.

If you want to skip the setup of ESLint CLI, the project set up with Babel and Webpack and the installation of ESLint plugin for VS Code (that’s a lot of things to skip) you can use the following links:

Let’s start by configuring ESLint.

ESLint configuration

We’ll start by creating a new project. We’ll create a folder and name it eslint-with-prettier. You can obviously name it whatever you like. We open that folder with Visual Studio Code, which is my favorite code editor. The first thing we do is to initialize an NPM project. As you can imagine we need Node installed for that. I’ll be using yarn as a package manager but you can use NPM if you prefer. We initialize our NPM project with the default options (the “-y” option):

yarn init -y
#or
npm init -y

Then we’ll install ESLint as a dev dependency.

yarn add -D eslint
#or
npm i -D eslint

After that’s done, we’ll use a really cool feature of ESLint which the ability to initialize itself by answering some questions. We can use a popular style guide or create our own. In our case, we’ll use Airbnb’s style guide for JavaScript and for React. The ESLint initialization process can also install the peer dependencies our styling guide requires. This is really helpful because the popular guides depend on quite a lot of packages. So let’s initialize ESLint and answer some questions:

yarn eslint --init
#or
npx eslint --init

In the first question we answer “Use a popular style guide” and then we select “Airbnb”. We’ll be using React so answer “y” to that question and then we’ll select the JSON format for our configuration. ESLint will then ask if we want it to install the peer dependencies with NPM and we’ll respond with “yes please!“. After that’s done this should be the content of our package.json file:

package.json
{
  "name": "eslint-with-prettier",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "eslint": "^5.11.1",
    "eslint-config-airbnb": "^17.1.0",
    "eslint-plugin-import": "^2.14.0",
    "eslint-plugin-jsx-a11y": "^6.1.2",
    "eslint-plugin-react": "^7.12.2"
  }
}

We can see that it installed 3 plugins and of course the Airbnb config. Those are the two types of packages that you encounter when using ESLint, configurations and plugins. Keep in mind that if we didn’t need React, we would have installed the eslint-config-airbnb-base instead, not eslint-config-airbnb. Just saying… And this should be our .eslintrc.json file:

eslintrc.json
{
  "extends": "airbnb"
}

Note here, that you can omit the .json extension from the ESLint configuration file name. So we’ll take advantage of that because it looks better and because I’m lazy and I want to write fewer things if possible.

Cool! Let’s now create some scripts in our package.json to help us run ESLint a bit easier. The first script will run ESLint in our src directory on .js or .jsx files and point out lint errors in the console. The second one will do the same thing after it tries to fix them first (with the –fix flag):

package.json
{
  "name": "eslint-with-prettier",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "lint": "eslint --ext js,jsx src",
    "lint:fix": "eslint --ext js,jsx src --fix"
  },
  "devDependencies": {
    "eslint": "^5.11.1",
    "eslint-config-airbnb": "^17.1.0",
    "eslint-plugin-import": "^2.14.0",
    "eslint-plugin-jsx-a11y": "^6.1.2",
    "eslint-plugin-react": "^7.12.2"
  }
}

Before we proceed further with ESLint and Prettier we’ll take a setback to create a simple react project. I know this sucks, but we have to do it because we need some code to test our tools against. If you already have a project you’ll probably good to go. Also if you are familiar with Babel and Webpack you can skim through the content or go directly to the installation of ESLint VS Code plugin.

A simple React project with Webpack and Babel

As you can imagine, for a React project we’ll need to install react and react-dom packages. We’ll also need Babel and Webpack to transpile and bundle our code respectively. Let’s begin installing our dependencies:

# regular project dependencies
yarn add react react-dom
# webpack dev dependencies
yarn add -D webpack webpack-cli webpack-dev-server babel-loader
# babel dev dependencies
yarn add -D @babel/core @babel/preset-env @babel/preset-react

We’ll start by configuring Babel first. We’ll create for that a .babelrc file in the root of our project. We’ll use 2 presets (think of them as a collection of plugins), env and react. The first is for transpiling regular JavaScript and the second is… for React.

.babelrc
{
  "presets": ["@babel/env", "@babel/react"]
}

And we’re done with Babel. Notice how we are omitting the “preset-” in the preset declaration, because they are already declared under the “presets” property. Now lets configure Webpack by creating a simple webpack.config.js configuration file:

webpack.config.js
const CleanWebpackPlugin = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");

module.exports = {
  entry: "./src/index.jsx",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
  mode: "development",
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /(node_modules)/,
        use: "babel-loader",
        resolve: {
          extensions: [".js", ".jsx"],
        },
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(["dist"]),
    new HtmlWebpackPlugin({ template: "./src/index.html" }),
  ],
};

As you can see, we are using the clean-webpack-plugin to clean our dist folder when building or when running the dev server. In addition to that, we’re using the html-webpack-plugin to append the bundle Webpack creates in our html. Let’s install both of those plugins:

yarn add -D clean-webpack-plugin html-webpack-plugin

Now let’s create the index.html file in our src folder that will reference our JavaScript bundle:

src/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0"
    />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>ESLint with Prettier</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

After that’s done, we’ll create the entry point of our React application which is the index.jsx file. We’ll place it inside the src directory:

src/index.jsx
import React from "react";
import ReactDOM from "react-dom";

const App = () => (
  <div>
    <h1>Configuring ESLint with Prettier!</h1>
  </div>
);

ReactDOM.render(<App />, document.getElementById("root"));

At this point, if you have the ESLint plugin for VS Code installed (we’ll install it in a bit), you may see a linting error that says “document is not defined”. To solve that we’ll go back to our .eslintrc file and add the “browser” as the environment:

.eslintrc
{
  "extends": "airbnb",
  "env": {
    "browser": true
  }
}

One last thing we have to do is to add 2 Webpack scripts in our package.json file. One will be for building our app in production and one for starting a Webpack development server to test our app locally:

package.json
{
  "name": "eslint-with-prettier",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "webpack-dev-server --hot --open --port=6000",
    "build": "webpack --mode=production",
    "lint": "eslint --ext js,jsx src",
    "lint:fix": "eslint --ext js,jsx src --fix"
  },
  "dependencies": {
    "react": "^16.7.0",
    "react-dom": "^16.7.0"
  },
  "devDependencies": {
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.2.3",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.4",
    "clean-webpack-plugin": "^1.0.0",
    "eslint": "^5.11.1",
    "eslint-config-airbnb": "^17.1.0",
    "eslint-plugin-import": "^2.14.0",
    "eslint-plugin-jsx-a11y": "^6.1.2",
    "eslint-plugin-react": "^7.12.2",
    "html-webpack-plugin": "^3.2.0",
    "webpack": "^4.28.3",
    "webpack-cli": "^3.2.0",
    "webpack-dev-server": "^3.1.14"
  }
}

And we’re done! We’ve successfully set up our React project. You came here to learn about ESLint and Prettier and you ended up making a custom boilerplate for React. But anyway, now we are able to run lint or lint:fix from our terminal and target some actual code:

yarn lint
#or
yarn lint:fix

By running the lint script from the console we’re now able to see some possible ESLint errors/warnings. If you see some, you can run the lint:fix script and hopefully the ESLint will fix them. That’s nice, but you can only see the lint errors in the console. If you want to see errors and warnings inside VS Code editor you’ll need a plugin for that.

ESLint plugin for VS Code

The plugin we need is called vscode-eslint and you can install it from the marketplace. You click the “extensions” icon on your left navigation bar inside VS Code and then you search for “eslint”. It should be the first result. You’ll need to reload the VS Code after you install in order to take effect. Now if you have lint errors in your code, you can see them highlighted inside the editor. Let’s now combine ESLint with Prettier by following the first and my personal favorite method.

1) Format with Prettier then fix with ESlint.

The good news is that in order to use Prettier, we don’t necessarily need an additional dependency in our project. On the contrary, we’ll use another VS code plugin that’s called prettier-vscode. We can install it the same way, from the “Extensions” area inside VS Code. This is the marketplace link. At this point, remember what we’re trying to achieve. We want to first run Prettier to format our code and the run ESLint with the –fix option to fix our linting errors. The prettier-vscode plugin does exactly that by using the prettier-eslint package under the hood. If we want those 2 plugins (vscode-eslint and prettier-vscode) to work nicely together, we’ll have to add some workspace options for VS Code in our project. We’ll do that by creating a folder named .vscode at the root of our project and inside that, we’ll add a settings.json file that looks like this:

.vscode/settings.json
{
  "prettier.eslintIntegration": true,
  "editor.formatOnSave": false
}

The first option instructs the Prettier plugin to read our .eslintrc configuration and format the code with respect to the linting rules. The second disables the formatting of our code on save which is my personal preference. We are now able to press Alt+Shift+F or right-click inside a file and then select the “Format Document” option and format our code! Objective completed. We configured ESLint and Prettier and we are ready to write some high-quality code.

Before we proceed any further, I want to add a final touch. I want to install as dev dependencies two libraries that are being used in the background by prettier-vscode plugin. Why do you want to do that, you may ask. I want to do it because the prettier-vscode plugin is not considered the most stable plugin ever (check the numerous Github issues). Sometimes it seems to ignore the ESLint configuration and doesn’t format the document from within the editor correctly (with Alt+Shift+F). As a result, we’ll install prettier-eslint and prettier-eslint-cli and create a script that’ll do the same thing the prettier-vscode plugin does. The only difference is that we’ll run that script from the terminal as opposed to formatting the files from the editor. Of course, if the prettier-vscode plugin works fine for us, we can use both of these methods. So let’s install those 2 dependencies:

yarn add -D prettier-eslint prettier-eslint-cli

and create a “format” script inside our package.json:

package.json
{
  // skipping stuff....
  "scripts": {
    "start": "webpack-dev-server --hot --open --port=6000",
    "build": "webpack --mode=production",
    "lint": "eslint --ext js,jsx src",
    "lint:fix": "eslint --ext js,jsx src --fix",
    "format": "prettier-eslint --write \"src/**/*.{js,jsx}\"" 
  }
  // skipping more stuff...
}

We can type yarn format in our terminal and see that it works as intended. Another use case for our format script is as a pre-commit script that can be used by tools like husky or pre-commit. This way we can prevent “bad code” from entering in our code base. Imagine someone forgetting to format the code from within the VS Code editor and only running the lint script. If we had only the lint script as a safety barrier in a pre-commit script, we would not have been able to prevent that commit.

At this point, let’s recap our first approach. We want to:

  1. write code
  2. run Prettier to format our code
  3. run ESLint with the –fix flag to fix our linting errors
  4. end up with pretty code that will not report any errors if we run our lint scripts.

We saw that we can achieve that by using the prettier-vscode plugin with the “format document” option inside the code editor. We also saw that we can create a format script and do the same thing from the terminal with the excellent prettier-eslint package directly. Let’s now see an alternative method which is to tell ESLint to run Prettier.

2) Run Prettier with ESLint.

I don’t really recommend this approach because it doesn’t respect 100% the coding style we chose to follow. This happens because Prettier, as we said, is a highly opinionated tool and regularly comes into conflict with the linting rules of our style guide.

With this method, we can keep whatever packages we have installed so far. There is no need to uninstall something. But we’re going to need some more!

We’ll follow the official guide from prettier documentation of “integrating ESLint with Prettier”. We see that the first option is to tell ESLint to run Prettier and show us the prettier errors the same way it shows us the ESLint errors. For that reason, we need the eslint-plugin-prettier and the original prettier package. Let’s install them:

yarn add -D prettier eslint-plugin-prettier

And our .eslintrc becomes:

.eslintrc
{
  "extends": "airbnb",
  "plugins": ["prettier"],
  "rules": {
    "prettier/prettier": "error"
  }
}

Now if you try to run the lint script with yarn lint you’ll see the Prettier errors in the console. And probably now they are a few… For example, these are mine:

  1:19  error  Replace `'react'` with `"react"`          prettier/prettier
  2:22  error  Replace `'react-dom'` with `"react-dom"`  prettier/prettier
  4:38  error  Replace `'root'` with `"root"`            prettier/prettier

That’s because we now have conflicting rules like the above. We see that Prettier wants double quotes for strings but Airbnb wants single quotes. Additionally, if you try to “format” the index.jsx with the Prettier VS Code plugin, probably nothing will happen. To fix that we’ll have to use another configuration package in addition to our original eslint-config-airbnb. That package is called eslint-config-prettier and we can install it with:

yarn add -D eslint-config-prettier

This configuration turns off the conflicting rules from ESLint and keeps those of Prettier. You can configure it in your .eslintrc:

.eslintrc
{
  "extends": ["airbnb", "prettier"], 
  "plugins": ["prettier"],
  "rules": {
    "prettier/prettier": "error"
  }
}

At this point you can configure it further in your .eslintrc file, to extend more things:

.eslintrc
{
  "extends": ["airbnb", "prettier", "plugin:prettier/recommended"], 
  "plugins": ["prettier"],
  "rules": {
    "prettier/prettier": "error"
  }
}

To be honest, I don’t really get the difference between just extending the “prettier” config versus extending the config AND the plugin. The result seems the same to me even with more “challenging” code. We can fix the errors from the console by running the lint:fix script or by right-clicking in the code and “Formatting the document” with the prettier-vscode plugin.

Conclusion

We saw that is not always easy to integrate ESLint with Prettier and get the best out of these tools. We demonstrated 2 different methods. In the first method, we format our code with Prettier and then fix the linting errors with ESLint. We do that with the help of the prettier-vscode plugin from inside VS Code or by using an NPM script with prettier-eslint package. The second method is to run Prettier from ESLint. We do that by using eslint-config-prettier and eslint-plugin-prettier. We can again format our code with the prettier-vscode plugin or by running the lint script with the –fix flag.

Other things to read

Popular

Previous/Next