In this article we'll use ESLint and Prettier to lint and format our code. We'll go through the configuration process and make sure the two libraries play nicely together.
This is part two of the series Create a React App From Scratch With Webpack and Babel, but you don't need to have read part one to follow along.
You can read part one here.
(This series has a companion Git repository: github.com/nicoqh/react-boilerplate)
Coding style guides help you avoid stylistic inconsistencies and potential problems in your code base.
A style guide consists of a set of rules. Using these rules, a linter or a formatter analyzes your source code and flags (or automatically fixes) potential problems and stylistic errors.
Rules are generally divided into two categories:
-
Formatting rules enforce the formatting of your code (how your code looks). For example, the rule "lines should be no longer than 80 characters" is a rule that affects how your code is formatted, but it doesn't affect the code's runtime behavior.
-
Code quality rules enforce coding (best) practices and aim to reduce bugs and incorrect code. For example, the rule "eliminate unused variables" is a rule that helps you remove unused variables from your code base (these variables could be left over from a refactoring). The rule has nothing to do with formatting; the code can be perfectly formatted even though it contains an unused variable.
We'll use two popular tools to lint and format our source code: ESLint and Prettier.
Linting with ESLint
ESLint is the most popular linter for JavaScript. It analyzes your code to find both stylistic (formatting) errors and code quality issues.
Before we start using ESLint, let's go through some important concepts.
ESLint offers a long list of rules, but none
are enabled by default. To use a rule, you add it to an ESLint configuration
file. For example, if you want to eliminate variables that are declared but not
used, add the no-unused-vars
rule to your .eslintrc.js
configuration file:
rules: {
"no-unused-vars": "error"
}
Configuration files can be shared between projects. Instead of maintaining your
own list of rules, you can extend another configuration file. Many
configuration packages have been published on npm. These are named
eslint-config-<name>
by convention.
To extend a configuration package, your create an extends
array in your ESLint
config file, and add the name of the config package (omitting the
eslint-config-
prefix):
extends: [
"standard" // Extend eslint-config-standard
],
rules: {
"no-unused-vars": "error" // Your own rules
}
Your own rules take precedence over the extended configurations.
What if you need rules that aren't provided by ESLint?
In addition to the rules provided by ESLint, there are several ESLint plugins
that can help you with specific enforcements. Plugin packages are named
eslint-plugin-<name>
by convention, and you can omit the eslint-plugin-
prefix when referencing them in your own ESLint config.
For example, the plugin eslint-plugin-react
provides a set of rules that are
useful for React and JSX projects. Adding this plugin to your .eslintrc.js
config makes its rules available for use:
plugins: [
"react", // eslint-plugin-react
],
rules: {
"react/no-typos": "warn", // Use the 'no-typos' rule from eslint-plugin-react
}
In addition to providing custom rules, ESLint plugins often contain configuration files. For example, the React plugin provides both React specific rules, and configuration files that use those rules. This is a convenient way to provide users with a recommended way of using the provided rules.
These plugin-provided configurations can also be extended from your ESLint config:
extends: [
"plugin:react/recommended", // A configuration from the React plugin
]
plugins: [
"react", // eslint-plugin-react
],
rules: {
"react/no-typos": "warn", // Your own rules
}
The syntax of the extends
array can be a bit confusing. How you reference
an external configuration depends on whether it's a local file, a configuration
package or a configuration provided by a plugin. You can
read more about the "extends" property
if you're interested. Here's an overview of the differences:
extends: [
"./myConfigFile.js", // Local file
"standard", // Configuration npm package `eslint-config-standard`
"plugin:react/recommended" // A configuration from a plugin
]
Let's get started by installing ESLint locally:
npm install eslint --save-dev
We won't add any rules or configurations manually. Instead, we'll let ESLint generate a config based on some sensible defaults.
By answering a set of questions, ESLint can install any necessary plugins and configurations, and then automatically create a configuration file.
To set up a configuration file, run the ESLint initialization command:
./node_modules/.bin/eslint --init
ESLint will ask you to install @eslint/create-config
.
Then you will be presented with a set of questions. I answered them like this:
- How would you like to use ESLint? To check syntax and find problems
- What type of modules does your project use? JavaScript modules (import/export)
- Which framework does your project use? React
- Does your project use TypeScript? No
- Where does your code run? Browser
- What format do you want your config file to be in? JavaScript
Answer yes when it asks to install the dependencies required by the configuration.
Your ESLint config file has been written to .eslintrc.js
. If you open the file
you'll see that it extends eslint:recommended
and plugin:react/recommended
.
At the bottom you'll find an empty rules
object. You can use this to add
additional rules, or to overwrite the ones provided by the recommended configs
that you're extending.
To see the result of your config file and all the active rules, run ESLint with
the --print-config
flag:
./node_modules/.bin/eslint --print-config .eslintrc.js
Let's test ESLint by intentionally adding an error to our code. Open
src/index.js
and define a variable:
const name = "Jon Snow";
Because this variable isn't used, ESLint will complain due to the
no-unused-vars
rule.
❯ ./node_modules/.bin/eslint src/index.js
/src/index.js
4:7 error 'name' is assigned a value but never used no-unused-vars
If you want to disable this rule (you probably shouldn't), add it to the rules
property in your .eslintrc
.
This is how you override rules from the configuration(s) you're extending.
To integrate ESLint with your code editor, check out the available integrations.
You can also add it as an npm script in package.json
:
"scripts": {
// ...
"lint": "eslint ./src"
}
This lets you simply run npm run lint
.
Formatting with Prettier
Prettier is a code formatter. It parses your code and re-prints it according to a set of opinionated formatting rules. Prettier doesn't care about the meaning of the code, only about how it looks.
Prettier can enforce consistent maximum line lengths, make sure you don't mix single and double quotes, add trailing commas to each array item, and help you with other formatting concerns.
Let's install Prettier:
npm install prettier --save-dev --save-exact
(The Prettier documentation recommends using the --save-exact
flag, which
saves an exact version to your package.json
instead of a version range.)
Then, run Prettier on a JavaScript file using the default formatting rules:
./node_modules/.bin/prettier src/index.js
Prettier will parse the code and re-print it according to its default rules. The command's output is the new code, correctly formatted. It didn't change (write to) the file, because we didn't tell it to.
Instead of manually running Prettier from the command line, check out the official documentation on how to integrate Prettier with your text editor.
Prettier formatting options
Prettier offers a set of customizable formatting options (rules about how the code should be formatted). The number of options is intentionally limited in order to avoid bike-shedding (arguing over trivial stuff). Due to the limited number of formatting options, Prettier can be seen as a style guide as well as a formatter. A big selling point for Prettier is that you don't need to argue with your co-workers on how the code should be formatted.
In this guide we'll use Prettier's default formatting options, except for one: use single quotes (') instead of double quotes (").
Prettier has a "singleQuote" option that we can use.
Create the config file .prettierrc
(notice the leading dot) and add the
following JSON (view commit):
{
"singleQuote": true
}
This will tell Prettier to prefer single quotes.
Using Prettier with ESLint
As mentioned, ESLint can detect both stylistic (formatting) errors and coding errors.
The recommended approach is to use Prettier for code formatting, and let ESLint take care of code quality concerns.
This means we should disable any existing formatting rules in ESLint. If you're extending other ESLint configuration, this can be hard to do manually.
To achieve this, Prettier provides an ESLint configuration package that disables all formatting rules that conflict with Prettier.
Install eslint-config-prettier
:
npm install --save-dev eslint-config-prettier
In order for this configuration to overwrite and disable ESLint formatting
rules, it must be the last configuration we extend from .eslintrc.js
:
// ...
extends: [
"eslint:recommended",
"plugin:react/recommended",
// Disables formatting rules from *all* ESLint configs.
// Provided by `eslint-config-prettier`.
"prettier",
],
// ...
In summary, we still extend some ESLint configurations, but we let Prettier's ESLint config disable any formatting rules from ESLint. Now, only Prettier is responsible for code formatting concerns.
You should integrate Prettier with your text editor so that your code is formatted automatically.
(It's also possible to have ESLint report formatting errors detected by Prettier.
The ESLint plugin eslint-plugin-prettier
runs Prettier as an ESLint rule and reports formatting errors as individual
ESLint issues. We won't do that in this article, but check it out if it makes sense to your
workflow.)
That's it for now. This series of articles will likely be expanded in the future to cover more Wepack, Babel and React concepts. For now, you can check out the bottom of the first article for tips on what to do next.