Set up ESlint, Prettier & EditorConfig without conflicts
François Hendriks11 min read
This is the second post of a series of articles on how to empower your dev environment using ESLint, Prettier and EditorConfig. If you have doubts regarding the use of all three tools, you may read my first article on Why You Should Use ESLint, Prettier & EditorConfig. The aim of this post is to provide guidelines on how to configure ESLint, Prettier and EditorConfig without any conflicts while also avoiding redundant configuration.
Context
If you have already used Prettier alongside ESLint, you probably encountered problems with your code formatting.
I encountered these issues at work when we decided to migrate from TSLint to ESLint on one of our TypeScript projects. The idea was to upgrade our linting capabilities by using both ESLint and Prettier. We patched the issue above by adding an ESLint rule forcing two space indentations. However, other conflicts appeared and it became clear we could not patch them all by adding an ESLint rule for each conflict.
While a lot of online resources on this topic exist out there, most just give out a configuration that works for very specific projects and fail to give an in depth explanation of why ESLint, Prettier or EditorConfig can cause formatting problems. The objective here is to understand the underlying issues so that any bug related to ESLint and prettier will not take forever to debug.
The pattern
We want to use the right tool for the right issue as discussed in the previous article on Why You Should Use ESLint, Prettier & EditorConfig. In this particular case, ESLint will be our code quality
linter, Prettier our code formatting
tool while EditorConfig will provide everyone with the right editor configuration.
- All configuration related to the editor (end of line, indent style, indent size…) should be handled by EditorConfig
- Everything related to
code formatting
should be handled by Prettier - The rest (
code quality
) should be handled by ESLint
ESLint and Prettier
For the rest of this post, we will use the code already written in the first article. To sum it up, we had a main.js
file alongside an ESLint configuration with Prettier installed.
main.js
function printUser(firstName, lastName, number, street, code, city, country) {
console.log(`${firstName} ${lastName} lives at ${number}, ${street}, ${code} in ${city}, ${country}`);
}
printUser('John', 'Doe', 48, '998 Primrose Lane', 53718, 'Madison', 'United States of America');
eslintrc.json
{
"extends": ["eslint:recommended"],
"env": {
"es6": true,
"node": true
}
}
Let Prettier do its job
In order to be able to use ESLint alongside Prettier, the idea is to deactivate all ESLint rules that might conflict with Prettier (code formatting
rules). Fortunately for us, the eslint-config-prettier
package already does that.
npm install eslint-config-prettier --save-dev
We will rewrite our .eslintrc.json
file by adding prettier
to the extends
array and removing any code formatting
rules we had:
{
"extends": ["eslint:recommended", "prettier"],
"env": {
"es6": true,
"node": true
}
}
The prettier
configuration will override any prior configuration in the extends array disabling all ESLint code formatting
rules. With this configuration, Prettier and ESLint can be run separately without any issues.
Integrate Prettier with ESLint
The process of having to run two commands to lint and format our file is not very convenient. To fix this, we will integrate Prettier with ESLint by adding the eslint-plugin-prettier
package.
npm install eslint-plugin-prettier --save-dev
We will now rewrite our .eslintrc.json
file by adding the prettier plugin in the plugins
array and setting the newly established prettier
rule to error
so that any prettier formatting error is considered as an ESLint error.
{
"extends": ["eslint:recommended", "prettier"],
"env": {
"es6": true,
"node": true
},
"rules": {
"prettier/prettier": "error"
},
"plugins": [
"prettier"
]
}
We take our unlinted main.js
file and apply ESLint on it:
npx eslint main.js
We can see that the lines that have too many characters or bad indentation, are marked by prettier/prettier
as errors within our ESLint error output.
Now let’s use the fix option of ESLint:
npx eslint --fix main.js
You will see that our file gets formatted the same way Prettier did.
You can replace all the prettier relevant configuration we made in our .eslintrc.json
file by adding the plugin:prettier/recommended
configuration in the extends array.
Here is what it would look like:
.eslintrc.json
{
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
"env": {
"es6": true,
"node": true
}
}
This configuration is the same as before but shorter and less explicit. For the sake of clarity, we will keep the first configuration for the rest of this post.
Common issues between Prettier and ESLint
Adding an ESLint plugin
This setup is all nice and good with this kind of small project but eventually, when one starts to use Vue, React or other frameworks, it becomes very easy to mess with both the ESLint and Prettier configurations. A common problem that I encounter a lot happens when developers add an ESLint plugin and expect it to work without adding anything else to make Prettier work.
Example with TypeScript
At some point in the project above, we decided to use TypeScript. Since TSLint will soon be deprecated, we decided to replace it with ESLint. We are using TypeScript as an example. What we do below is applicable to React, Vue or any other frameworks that have an ESLint plugin available.
Here is our main.ts
file inspired from our last main.js
file:
function printUser(firstName: string, lastName: string, number: number, street: string, code: number, city: string, country: string): void {
console.log(`${firstName} ${lastName} lives at ${number}, ${street}, ${code} in ${city}, ${country}`);
}
printUser('John', 'Doe', 48, '998 Primrose Lane', 53718, 'Madison', 'United States of America');
To make ESLint compatible with TypeScript or any other framework with specific syntax, we need to add a parser so that ESLint can read the new code and a new set of rules relevant to TypeScript (or any another framework that requires a parser) within a plugin.
npm install typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev
We then modify our .eslintrc.json
file to incorporate TypeScript by adding the TypeScript parser in the parser
option and by adding the plugin to our extends
array:
{
"parser": "@typescript-eslint/parser",
"extends": ["plugin:@typescript-eslint/recommended", "eslint:recommended", "prettier"],
"env": {
"es6": true,
"node": true
},
"rules": {
"prettier/prettier": "error"
},
"plugins": [
"prettier"
]
}
We then try ESLint on our file with the fix option:
And if we run the command multiple times, we get the same error even though the console says the errors can be fixed. The file does change and becomes:
function printUser(
firstName: string,
lastName: string,
number: number,
street: string,
code: number,
city: string,
country: string
): void {
console.log(
`${firstName} ${lastName} lives at ${number}, ${street}, ${code} in ${city}, ${country}`
);
}
printUser(
'John',
'Doe',
48,
'998 Primrose Lane',
53718,
'Madison',
'United States of America'
);
So Prettier definitely formatted our code but somehow ESLint expects an indentation of 4 spaces but gets 2 from Prettier. It looks like the error comes from a @typescript-eslint
rule.
If like me, you use VSCode and have the auto-fix on save option for ESLint on, the issue will look like this:
Fixing the conflict by disabling all ESLint formatting rules of the added plugin
Now, a common mistake a lot of people make is try to fix this issue or any other issue regarding conflicts with Prettier after the addition of a new plugin by overriding the rule that generates the conflict. In this case it is the indent rule of the @typescript-eslint
plugin. To do this, they add this rule to the rules
array:
"@typescript-eslint/indent": ["error", 2]
This effectively solves the problem by forcing the typescript-eslint
indent rule to match the indentation rule of prettier. But two problems appear:
- For now we only have a problem with the indentation, we do not know if there are any other rules in the
typescript-eslint
plugin that will conflict with Prettier - Now both ESLint and Prettier will be responsible for formatting our code. This is not the pattern we want to follow
If we follow the pattern described above, no code formatting
should be done by ESLint and the newly added plugin is no exception to this pattern. We therefore need to disable all the code formatting
rules of the added plugin by adding prettier/@typescript-eslint
to our extends
array.
{
"parser": "@typescript-eslint/parser",
"extends": ["plugin:@typescript-eslint/recommended", "eslint:recommended", "prettier", "prettier/@typescript-eslint"],
"env": {
"es6": true,
"node": true
},
"rules": {
"prettier/prettier": "error"
},
"plugins": [
"prettier"
]
}
Please keep in mind that the .eslintrc.json
extends
array’s order is very important. Basically each time a new configuration is added to the array, it will override the previous configurations. It is therefore of the utmost importance that prettier
and prettier/@typescript-eslint
are at the end of the array.
With this configuration, no more problems appear. We can rest assured that ESLint will not try to do Prettier’s job.
To sum up this common problem:
- Whenever you add a plugin in ESLint, you have to think about the
code formatting
rules that it adds and add aprettier
configuration (if available) to disable them all. In our case, we usedprettier/@typescript-eslint
but we could have usedprettier/react
orprettier/vue
. Check out the eslint-config-prettier documentation to get the list of supported ESLint plugins. - Do not try overriding the formatting rules by yourself in the
.eslintrc.json
file (it is not ESLint’s role) - If you see your code being formatted in two different ways with Prettier and ESLint conflicting, it means you have a useless ESLint formatting rule generating this conflict and that you do not follow the pattern described above
Adding a custom rule
For our project above, the team is not comfortable with 2 space indentation and wants to switch to 4. A common mistake is to forget our ESLint-Prettier pattern and apply this rule within the .eslintrc.json
file like so:
{
"parser": "@typescript-eslint/parser",
"extends": ["plugin:@typescript-eslint/recommended", "eslint:recommended", "prettier", "prettier/@typescript-eslint"],
"env": {
"es6": true,
"node": true
},
"rules": {
"prettier/prettier": "error",
"@typescript-eslint/indent": ["error", 4]
},
"plugins": [
"prettier"
]
}
The generated error should be of no surprise to you if you followed everything that was said before. We have the exact same error as in the previous issue with a conflict between Prettier and ESLint:
There are two problems:
- Since our custom rule is defined within our own
rules
array of the.eslintrc.json
file, it overrides theprettier/@typescript-eslint
configuration that is responsible for disabling all formatting rules - Once again, by adding this rule, we just forget about Prettier and do not follow our pattern
To follow this pattern, all code formatting
rules should be set within Prettier’s configuration in the .prettierrc
file. In our case, we should add one now:
.prettierrc
{
"tabWidth": 4
}
Now Prettier will format your code using 4 spaces instead of the default 2 while the .eslintrc.json
file should be exempt of any rules regarding indentation.
To sum up this common issue:
- Whenever you want to add a rule, try and categorize the rule within the
code quality
orcode formatting
sets. To make this easier, you just have to check whether the rule is supported and enforcable by prettier - Do not add custom formatting rules to your
.eslintrc.json
file. These will almost certainly conflict with Prettier
Prettier and EditorConfig
Setting up the editor configuration
EditorConfig allows us to have the same editor configuration regardless of the editor used. We thus do not have to rely on Prettier to format our code with the team’s conventions each time new code is written. It does require you to have the necessary EditorConfig plugin or extension installed on your IDE.
In this article, we will use VSCode but keep in mind that EditorConfig supports a variety of editors.
We add a custom editor configuration to our project:
.editorconfig
[*]
end_of_line = lf
charset = utf-8
indent_style = space
If you use the EditorConfig VSCode extension with this configuration, the editor will automatically know how to format your files. You will notice this at the bottom right of the editor:
Avoiding redundant configuration between EditorConfig and Prettier
However, this means that Prettier and EditorConfig share some configuration options that we do not want to repeat in two separate configuration files and keep them in sync (e.g. end of line configuration). The latest versions of Prettier address this issue by parsing the .editorconfig
file to determine what configuration options to use.
Those options are limited to:
end_of_line
indent_style
indent_size/tab_width
max_line_length
These configuration options will override the following Prettier options (if they are not defined in the .prettierrc
):
"endOfLine"
"useTabs"
"tabWidth"
"printWidth"
As it was the case for ESLint and Prettier, if you wish to change the configuration, the rule is to check whether it is a EditorConfig or Prettier relevant configuration and then change it in the appropriate file. The previous configuration options should be written only in the .editorconfig
.
If you followed what we have done until now, you might notice a configuration error. We changed the indentation size in our Prettier configuration. Since this is an editor relevant configuration, we should add it to the .editorconfig
file.
.editorConfig
tab_width 4
We remove the .prettierrc.json
file and run ESLint on our unformatted code:
function printUser(
firstName: string,
lastName: string,
number: number,
street: string,
code: number,
city: string,
country: string
): void {
console.log(
`${firstName} ${lastName} lives at ${number}, ${street}, ${code} in ${city}, ${country}`
);
}
printUser(
"John",
"Doe",
48,
"998 Primrose Lane",
53718,
"Madison",
"United States of America"
);
The code’s indentation is at 4. Now, whenever you open a new file with your editor, the indentation will already be configured at 4. Prettier will not have to format your code for this setting.
I hope this article will help you configure ESLint, Prettier and EditorConfig on your own projects. It took me some time to figure it all out. With this type of configuration, I think it would be best not to adopt a setup once and forget approach for your linting and formatting experience. If you wish to modify your configuration, you will have to remember following the pattern described above.