Gear up your React codebase with Prettier and ESLint Automation
Tags: react, webdev, javascript, typescript
Published: 2021-09-06 10:42:24

Article originally posted on [my blog](https://www.ankurkedia.com/blog/react-starter).

--- 

Recently, I noticed that there were repetitive tasks that I was doing for all the new projects. So, I decided to document them so as not to repeat the mistakes. This article is about setting up [Prettier](https://prettier.io), [ESLint](https://eslint.org), [Husky](https://typicode.github.io/husky) in your project.

## This article can help you with:

* Keeping your code clean.
* Following standard practices in your code.
* Ensuring that all the tests pass before commit.
* Improving commit quality by adhering to the [conventional commit format](https://www.conventionalcommits.org).

## Steps to setup

* Create a React app any way you like. I prefer to do it with `NPX`.

For TypeScript project:
```sh
npx create-react-app my-app --template typescript
```

For JavaScript project:

```sh
npx create-react-app my-app
```

You can also use `Yarn` or `NPM`. You can check out [CRA docs](https://create-react-app.dev/docs/getting-started#creating-an-app) for more details.

* Open the project: `cd my-app`.

*  Installing dependencies: 

With TypeScript:
 
```sh
yarn add -D eslint eslint-plugin-react prettier eslint-plugin-jest eslint-plugin-react-hooks @typescript-eslint/eslint-plugin @typescript-eslint/parser husky @commitlint/{config-conventional,cli}
```

The versions of packages at the time of writing are:
```json
{
  "devDependencies": {
    "@commitlint/cli": "^13.1.0",
    "@commitlint/config-conventional": "^13.1.0",
    "@typescript-eslint/eslint-plugin": "^4.29.0",
    "@typescript-eslint/parser": "^4.29.0",
    "eslint": "^7.32.0",
    "eslint-plugin-jest": "^24.4.0",
    "eslint-plugin-react": "^7.24.0",
    "eslint-plugin-react-hooks": "^4.2.0",
    "husky": "^7.0.1",
    "prettier": "^2.3.2"
  }
}
```

With JavaScript: 

```sh
yarn add -D eslint eslint-plugin-react prettier eslint-plugin-jest eslint-plugin-react-hooks husky @commitlint/{config-conventional,cli}
```

The versions of dependencies at the time of writing are:

```json
{
  "devDependencies": {
    "@commitlint/cli": "^13.1.0",
    "@commitlint/config-conventional": "^13.1.0",
    "eslint": "^7.32.0",
    "eslint-plugin-jest": "^24.4.0",
    "eslint-plugin-react": "^7.24.0",
    "eslint-plugin-react-hooks": "^4.2.0",
    "husky": "^7.0.1",
    "prettier": "^2.3.2"
  }
}
```

* Add Prettier config: create `.prettierrc` with this content. Also, install the [Prettier plugin](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) in VSCode.

```json
{
  "singleQuote": true,
  "jsxBracketSameLine": false,
  "useTabs": false,
  "eslintIntegration": false,
  "tslintIntegration": true,
  "requireConfig": false,
  "stylelintIntegration": false,
  "arrowParens": "always",
  "bracketSpacing": true,
  "embeddedLanguageFormatting": "auto",
  "htmlWhitespaceSensitivity": "css",
  "insertPragma": false,
  "jsxSingleQuote": true,
  "tsxSingleQuote": true,
  "printWidth": 80,
  "proseWrap": "preserve",
  "quoteProps": "as-needed",
  "requirePragma": false,
  "semi": true,
  "tabWidth": 2,
  "trailingComma": "es5"
}
```
> Note: I prefer this config, you can use [Playground](https://prettier.io/playground/) and choose what works for you. You can check out the [rationale](https://prettier.io/docs/en/rationale.html) and the [options](https://prettier.io/docs/en/options.html) to understand the rules better.

* Add ESLint config: create `.eslintrc.json` with this config. Also, install the [ESLint plugin](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) in VSCode.

For TypeScript: 
```json
{
  "settings": {
    "react": {
      "version": "detect"
    }
  },
  "env": {
    "browser": true,
    "es2021": true,
    "node": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react-hooks/recommended"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": 12,
    "sourceType": "module"
  },
  "plugins": ["react"],
  "rules": {
    "no-bitwise": 0,
    "react/react-in-jsx-scope": "off",
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    "@typescript-eslint/ban-ts-comment": "off",
    "@typescript-eslint/explicit-module-boundary-types": "off",
    "@typescript-eslint/no-explicit-any": "off"
  }
}
```

For JavaScript:

```json
{
  "settings": {
    "react": {
      "version": "detect"
    }
  },
  "env": {
    "browser": true,
    "es2021": true,
    "node": true,
    "jest": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended"
  ],
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": 12,
    "sourceType": "module"
  },
  "plugins": ["react"],
  "rules": {
    "no-bitwise": 0,
    "react/react-in-jsx-scope": "off",
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn"
  }
}
```

The rules I have used are basically to override the recommended settings. The only addition being the [rules of hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks).

> Note: Again, these are rules that I prefer, you can add the ones that work for you. You can check [ESLint configuration](https://eslint.org/docs/user-guide/configuring/#specifying-environments) docs to [create your rules](https://eslint.org/demo).


*  Add scripts to `package.json`

For TypeScript:

```json
{
  "scripts": {  
    "lint": "eslint \"**/*.{ts,tsx,js,jsx}\"",
    "prepare": "husky install",
    "test": "react-scripts test"
  }
}
```

For JavaScript:

```json
{
  "scripts": {  
    "lint": "eslint \"**/*.{js,jsx}\"",
    "prepare": "husky install",
    "test": "react-scripts test"
  }
}
```


* Resolve lint errors: run `yarn lint` and resolve all warnings and errors (if any). You might see warnings and errors like this:

```bash
warning  'temp' is assigned a value but never used  @typescript-eslint/no-unused-vars

✖ 1 problem (0 errors, 1 warning)
```

* Add Commitlint config: 

```sh
echo "module.exports = {extends: ['@commitlint/config-conventional']};" > commitlint.config.js
```

Conventional config is used here, you can also define your config with different [rules](https://commitlint.js.org/#/reference-rules).

* Install git hooks `yarn prepare / npm prepare`

> Note: Using Husky with Yarn 2 requires [different steps](https://typicode.github.io/husky/#/?id=yarn-2).


* Add hooks: you can add any number of hooks you need. In this case, we are adding a pre-commit hook that will check the linting and run tests. We are also adding a Commitlint hook.

```sh
npx husky add .husky/pre-commit 'yarn lint && yarn test --watchAll=false'
```

```sh
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit $1'
```


For more specific use cases for husky, you can check the [recipes](https://typicode.github.io/husky/#/?id=recipes).

Et voila, this is all you needed to do.
You can test the hook by simply committing. `git commit -m "foo: this will fail"`. You should see this in the terminal.

```sh
⧗   input: foo: this will fail
✖   type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum]

✖   found 1 problems, 0 warnings
ⓘ   Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint

husky - commit-msg hook exited with code 1 (error)
```
> Note: If you want to skip these hooks, you can commit directly with `-n/--no-verify` option like `git commit -m "yolo!" --no-verify`

If you are starting with a new project, you can clone these repos with all the steps already done.

{% github theankurkedia/react-starter-ts-template no-readme %}
{% github theankurkedia/react-starter-template no-readme %}