Linting & Formatting in React and React Native

The minimal way to get up and running in 2022 with ESLint, Prettier, VSCode, Husky

Manu Rana
6 min readJun 1, 2022

If you are here, let’s assume that you know how linting and formatting can help you write and maintain a cleaner JS/TS codebase. If not, read about it here, here and here. (just go through their intros for now)

This opinionated post is specifically an attempt to simplify the process of configuring ESLint (linter) and Prettier (formatter) for newly created React or React Native apps, using the respective typescript templates.

Objectives

Let’s talk about when (Use Cases) you will need these capabilities in your dev lifecycle, and how to do the MINIMAL configuration necessary to get started for each of these scenarios. The aim is to get you up and running quickly.

The React and React Native teams maintain default configs, and we want to leverage them as much as possible.

Use Cases

There are 3 use cases where we need to invoke the ESLint+Prettier combo. All three will share the same configuration, and invoke ESLint+Prettier in the same way, to avoid conflicts.

  1. While working on a file in the editor.
  2. Feedback while editing (squigglies, anyone?)
  3. Automatically fixing both formatting and linting errors (when possible) while saving
  4. While making a commit, where you are checking all the STAGED files
  5. A batch process where you check the COMPLETE CODEBASE for any issues

Sharing the configuration is easy. Both eslint and prettier support configuration through very similar means, which can be used for all three situations. I prefer using JS files for each, .eslintrc.js and prettierrc.js

Pretter vs ESLint : Domains of responsibility, and integration

Prettier has a very clear philosophy on how it compares with ESLint. It aims to replace the formatting-only rules inESLint(the linter), while retaining and leveraging the code-quality rules from ESLint.

It also talks about how it should integrate with ESLint. Two options are laid out.

1. Run Prettier independently of ESLint (recommended)

This involves two steps

  1. Run prettier on the code for pure formatting
  2. Run eslint on the code for code-quality rules only, disabling all formatting-related rules by using eslint-config-prettier

2. Run Prettier as an ESLint plugin

The eslint-plugin-prettier is used for this. There is only one step that needs to run, which is ESLint, and it takes care of the linting as well as prettying. (The formatting-specific eslint rules still need to be disabled by using eslint-config-prettier) Prettier talks about why this is not recommended for typical use cases.

Initial Configuration

This is only needed for CRA generated apps

Create React App (CRA)

The default CRA typescript template does not have Prettier installed (but does have ESLint installed and configured). So let’s install Prettier and configure the ESLint+Prettier integration. We use the recommended integration strategy (run Prettier independently from ESLint)

yarn add -D prettier eslint-config-prettier

Now let’s modify the existing ESLint configuration to ignore all formatting related rules. The default eslint config is included in the package.json in the eslintConfig section. We will modify it to add "prettier" at the very END of the extends property.

{
"scripts": {
// ...
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest",
"prettier" // add this line
]
},
}

RN Cli

The standard RN app created by the CLI actually ships with a default ESLint+Prettier config. The default ESLint+Prettier config in RN runs Prettier as an ESLint plugin (not the recommended option, but works). We don’t have to do anything here.

Editor

We will specifically talk about VSCode

First, we will install the esbenp.prettier-vscode and dbaeumer.vscode-eslint extensions. By default, both extensions will pick up the respective project configs. We can now start seeing indications (squiggles) in our code based on our configurations, if an error is detected.

That takes us to saving. Next, let’s configure VSCode to automatically run the 2-step flow on save. VSCode gives us two separate hooks to run actions, one configurable and the other not so much.

The first one is editor.formatOnSave. This runs the default editor formatter whenever a file is saved (which we can configure to be Prettier). The other is editor.codeActionsOnSave. This accepts an array of code actions that are performed on every save. We can use this to run the ESLint Fix action.

There is one issue here. The editor.formatOnSave action runs AFTER everything in editor.codeActionsOnSave. Which means that the Prettier step will run after the ESLint step. But we want this to happen the other way round.

The esbenp.prettier-vscode extension does not (yet) offer a code action for editor.codeActionsOnSave, so we cannot add it to that hook (an issue and a PR are open)

So in the meantime, you can use another extension ( rohit-gohri.format-code-action ) which exposes the default VSCode editor.formatOnSave as a code action. Read more about it here.

Once we have installed it, we need to configure three things.

  1. We need to turn OFF editor.formatOnSave.
  2. We need to make sure that the default formatter is Prettier.
  3. We need to add this new action to editor.codeActionsOnSave, BEFORE the ESLint action. Here is how that works.
{
"editor.formatOnSave": false,
"editor.defaultFormatter": "esbenp.prettier-vscode", // this is the prettier extension
"editor.codeActionsOnSave": {
"source.formatDocument": true, // this is exposed by rohit-gohri.format-code-action
"source.fixAll.eslint": true // this is exposed by dbaeumer.vscode-eslint
},
}

Note that we can do this independently for different file types if we want to be selective. For example:

{
// Global
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
// JSX specific settings
"[javascriptreact]": {
"editor.formatOnSave": false, // false ONLY for this file type
"editor.codeActionsOnSave": [
"source.formatDocument",
"source.fixAll.eslint"
]
},
}

TIP: It sometimes helps to restart VSCode for it to pick up all the new configs and extensions.

Batch

This needs to be done through scripts in package.json that we can run on demand

CRA

We need to add scripts to lint (check), lint (fix) and prettier (fix)

"scripts": {
//... others
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
"prettify": "prettier --write ."
},

Always run yarn prettify first, then yarn lint:fix

RN Cli

There is an already existing script to lint (check). We just need to add another that will do the automatic fixes as well. (You may not need the typescript extensions, if you did not chose a typescript template)

"scripts": {
//... others
"lint": "eslint . --ext .js,.jsx,.ts,.tsx", // already exists
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix" // ADD this one
},

Run yarn lint:fix to run the ESLint+Prettier combo.

On Commit

We want to make sure that any code being committed is passing our linting rules, and all automatic fixes (including formatting) are being applied.

This is where we use Husky and Lint-Staged. Husky allows us to tap into git hooks (in this case pre-commit), and Lint-Staged allows us to run batch jobs on all staged files.

Lets follow the EASY instructions from Lint-Staged and do this. This incorporates the default Husky install, and is a superset.

npx mrm@2 lint-staged

This will automatically configure the project with most of what you need

Specifically, this will

  1. install Husky along with configuring a pre-commit hook to run Lint-Staged
  2. install Lint-Staged, and add a lint-staged entry in the package.json to configure what scripts run on the staged files, by glob patterns.

Now, depending on the app, we need to make the following changes

CRA

Modify the package.json to use the following config for lint-staged (prettify all files that prettier supports, and lint-fix only the js/ts code)

"lint-staged": {
"*.{js,jsx,ts,tsx,json,css,scss,md}": "prettier --write",
"*.{js,jsx,ts,tsx}": "eslint --fix"
}

RN Cli

Use this config in package.json (eslint invokes prettier on all js/ts files, prettify the rest of the files)

"lint-staged": {
"*.{json,css,scss,md}": "prettier --write",
"*.{js,jsx,ts,tsx}": "eslint --fix"
}

For the CRA app, all these settings and the resulting code is available here. For the RN app, the changes are minimal enough.

--

--