Setup ESLint and Prettier the right way

Before we dive into the topic, you might not know what ESLint and/or Prettier are. ESLint is a JavaScript and TypeScript linting tool. It scans your code and underline potential errors in red and warnings in yellow. It is very useful to improve your code quality and avoid bugs. Prettier is a formatting tool, it supports many languages (JavaScript, TypeScript, CSS, HTML, Markdown, JSON, Yaml, etc). It is useful to keep your code readable and make sure the code format stays consistent when working in a team.

I will first explain how to set them up correctly and run them manually in the terminal, then I will show you how to setup Visual Studio Code to automatically run ESLint and Prettier for you. Finally I will show you how to setup a pre-commit hook that will run ESLint and Prettier before you commit and cancel the commit if ESLint and/or Prettier detect something wrong with your code.

There are already many tutorials out there explaining how to achieve this, but most of them recommend using the eslint-plugin-prettier which basically runs Prettier through ESLint. That is not the recommended way according to the official Prettier documentation. Instead I will show you how to turn off conflicting ESLint rules and then you will be able to run ESLint and Prettier separately and get the best performance.

Before going further, make sure you have Node.js and NPM installed on your machine.

Let's setup a little project. Open your terminal and create a new directory somewhere in your machine

mkdir eslint-prettier-example

Move inside that directory

cd eslint-prettier-example

Initialize a NPM project

npm init -y

Install the following development dependencies

npm install --save-dev eslint eslint-config-prettier prettier

Create a configuration file for ESLint

touch .eslintrc

and add the following content

{
"root": true,
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
},
"extends": ["eslint:recommended", "prettier"],
"env": {
"browser": true,
"node": true
},
"rules": {
"no-console": "error"
}
}

The root property indicates that this configuration is the root of the project, because it is possible to use multiple ESLint configuration files.

The parserOptions defines what version of ECMAScript you use in your code and if you use modules or not (import / export syntax).

The extends property allows to add rules to the configuration. Here I use the recommended rules of ESLint and the rules defined by the eslint-config-prettier package, that simply override conflicting rules between ESLint and Prettier. The order is important, most of the time you will want to have the prettier rules last.

The env property defines which kind of environment your code will run in, if it is a Node.js application then set node to true if it will run in a browser (like a React app for example) then set browser to true.

Finally the rules property allows you to add/edit rules specifically. I like to add the no-console: "error" rule so that ESLint throws an error when I use console.log. Of course, I still use console.log but only while working locally, I never commit code with console.log unless it is for real logging cases and not for debugging.

There are more options available, have a look at the official ESLint documentation if you're interested.

For Prettier, I like to use the default configuration, but they also have a configuration file (.prettierrc) if you wish to force a specific code style. If you're interested, have a look at the official Prettier documentation.

Now let's add some scripts in the package.json so that we can run ESLint and Prettier manually

{
"scripts": {
"format:check": "prettier --check .",
"format:write": "prettier --write .",
"lint:check": "eslint .",
"lint:fix": "eslint --fix ."
}
}

You can now check if the formatting matches Prettier's rules by using

npm run format:check

And you can force the formatting by using

npm run format:write

You can also lint your code with

npm run lint:check

and auto-fix errors (when possible) with

npm run lint:fix

Try the commands by creating a new JavaScript file and adding some badly formatted code in it. If you've added the no-console: "error" ESLint rule, you can easily check that ESLint is working correctly by adding a console.log in that JavaScript file.

// example.js
// DO NOT ADD THE "prettier-ignore" comment below, I added it so that Prettier
// doesn't actually fix this example automatically for me
// prettier-ignore
function example() { console.log("Hello World!"); }

Now let's see how to configure Visual Studio Code to lint your code automatically and format when you save. If you use a different IDE, they might also have plugins for ESLint and Prettier but you will need to check how to configure them.

You will need to install the two following extensions:

There is an extra step to activate them. We need to edit the Visual Studio Code settings. I like to do this per workspace but you can also edit the settings globally.

Let's create a .vscode directory in the project

mkdir .vscode

Then add a settings.json file inside that directory

touch .vscode/settings.json

Add the following inside the .vscode/settings.json file

{
"editor.formatOnSave": true,
"files.autoSave": "onFocusChange",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}

The "editor.formatOnSave": true rule tells VSCode to run the code formatter (Prettier) when saving.

The "files.autoSave": "onFocusChange" rule tells VSCode to automatically save when changing window or opening a new file (when the focus on the current file is lost)

The "editor.defaultFormatter": "esbenp.prettier-vscode" rule tells VSCode to use the Prettier extension as the default code formatter.

The "editor.codeActionsOnSave" rule allows to define more actions to run when saving. In this case I added "source.fixAll.eslint": true which will run ESLint with the --fix parameter to attempt to automatically fix the errors.

Of course it is up to you to edit these settings to your liking, these are just my personal preferences.

Finally let's see how to configure a pre-commit hook to automatically run ESLint and Prettier before committing your code.

A pre-commit hook is a Git feature that allows to run a command before doing a git commit. To set it up we will use the husky package. We will also use the lint-staged package, so that we only run ESLint and Prettier on files that will be committed. If you're working on a big project it would be too slow to run ESLint and Prettier over the whole codebase.

You will need to install those two packages

npm install --save-dev husky lint-staged

Then, to enable the git hooks, use the following command

npx husky install

Run the following as well to make sure that Husky will be automatically setup when installing the dependencies (so that your teammates won't have to do all this)

npm set-script prepare "husky install"

And now let's create the pre-commit hook with the following command

npx husky add .husky/pre-commit "npx lint-staged"

Now, whenever you commit something, npx lint-staged will run before committing and if that command returns an error the commit will be cancelled.

This is useful if your teammates don't want to setup ESLint and Prettier in their IDE, at least they will be required to run them manually in order to commit.

We still need to configure lint-staged. Open the package.json and add the following

  "lint-staged": {
"**/*.{js}": [
"eslint"
],
"**/*.{js,json}": [
"prettier --check"
],
}

Of course make sure to adapt this to target all the possible file extensions you use in your project.

Now, when lint-staged is executed (automatically thanks to the pre-commit hook), it will run ESLint then Prettier for all the matching files that have been staged (about to be committed).

That's it, now you can focus on writing code and improving your code quality thanks to ESlint. You won't have to worry about formatting because Prettier will take care of it for you.

You can find the the full example on GitHub: https://github.com/ncribt/eslint-prettier-example