Setting up ESLint and Prettier using lint-Staged and Husky
Initial setup
Setting up ESLint
only one command is now needed to install and setup ESLint. In your terminal or PowerShell run:
npm init @eslint/config
this should add a file called .eslintrc.{js,yml,json}
with the basic config of
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"standard-with-typescript",
"plugin:react/recommended",
"next/core-web-vitals"
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
}
}
Setting up Prettier
npm install --save-dev --save-exact prettier
node --eval "fs.writeFileSync('.prettierrc','{}\n')"
add a new file in the root of your project called .prettierignore
now add and folders or files you don't want prettier to format
build
coverage
public
prettier configuration
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": true,
"singleQuote": true
}
Lets run through these settings in more detail
trailingComma
the options are all
, es5
or none
The es5
option will add a trailing comma where it is valid in ES5 for objects, arrays, etc... The default is all
tabWidth
how many spaces you want a tab to equal. I like 4 as it make the indents more noticeable and easier for me to read. The default is 2
semi
If true then it will add a semicolon to the end of every statement. When I first started to code I was bad at adding these. Now after coding with this enabled for a while I still miss the occasional one but I pick up in it when looking at someone else's code. The default is true`
singleQuotes
This is to use single quotes instead of double quotes, while this is a preference it is good to state the preference so others know what style is prefered. The default is false
now run the following command to format all your files
npx prettier . --write
ESLint configuration
Install plugins
To start with we are going to extend some other ESLint plugins:
npm install @typescript-eslint/eslint-plugin eslint-plugin-jsx-a11y eslint-config-prettier eslint-plugin-import eslint-config-airbnb eslint-config-airbnb-typescript --save-dev
eslint-plugin-jsx-a11y - checks to make sure your code follows the accessibility rules eslint-config-prettier - Turns off all rules that are unnecessary or might conflict with Prettier eslint-plugin-import - This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, and prevent issues with misspelling of file paths and import names eslint-config-airbnb - This style guide is mostly based on the standards that are currently prevalent in JavaScript
Configuring the .eslint.json file
Adding in the plugins we just installed
In the .eslint.json file we will stat by extending the plugins we have already installed.
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:jsx-a11y/recommended",
"airbnb",
"airbnb-typescript",
"prettier"
],
react-in-jsx-scope
The new version of React doesn't require you to add import React from "react"
if you are not using it so adding this allows you to remove that import
"react/react-in-jsx-scope": [
"off"
],
@typescript-eslint/indent
Displays a warning if the correct indentation is not used, this is also linked to the prettier configuration so make sure these match
"@typescript-eslint/indent": [
"warn",
4
],
import/extensions
If you import .js/.jsx/.ts/.tsx
files then you don't need to add the extension and it works fine. This is used so that there is consistency on file imports across your project. This would display an error if you never added extensions for scss
, svg
or JSON
files.
"import/extensions": [
"error",
"never",
{
"scss": "always",
"svg": "always",
"json": "always"
}
],
max-len
If you don't want long lines of code as they can be harder to read, then add the following rule. In this rule we are limiting the line to 120 characters but this can be adjusted to your liking, but we are going to ignore this rule if it is a URL or regex pattern
"max-len": ["error",
{
"code": 120,
"ignoreUrls": true,
"ignoreRegExpLiterals": true
}],
no-console
The no console rule is very good at catching console logs left behind from debugging. This can help stop any unintentional console.log
statement from making it into production. This rule also has an exception for console.error
so you can send any error messages to the console if needed
"no-console": [
"error",
{
"allow": [
"error"
]
}
],
comma-dangle & @typescript-eslint/comma-dangle
The next two are for the same thing but from different packages. They warn if a trailing coma is not added to the end of every line but only if they are spread over multiple lines and are not need if it a single line
"comma-dangle": [
"warn",
{
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "never"
}
],
"@typescript-eslint/comma-dangle": [
"warn",
{
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "never"
}
],
react/function-component-definition
Everyone has their favourite way to create a functional component but if you want to make sure they are all created the same then you can add this to your file, and this will force all the files to use arrow functions but options for the other methods are available.
"react/function-component-definition": [
"error",
{
"namedComponents": "arrow-function",
"unnamedComponents": "arrow-function"
}
]
react/require-default-props
If some of your props could be undefended then you will get an error asking for you to create defaultProps for it. DefaultProps are being going to be removed from a future release of JavaScript so it is best not to use them so you can turn them off by adding
"react/require-default-props": "off",
react/display-name
By default, React want you to set a display name for you function or class but this is usually for debugging and is usually inferred by the name of the function or class. So to stop you getting this error you can tunr it off by adding this rule:
"react/display-name": "off"
@typescript-eslint/explicit-function-return-type
By default, TypeScript wants you to declare the return type of each function even the pages so if you want to disable that then you can add
"@typescript-eslint/explicit-function-return-type": "off"
Final .eslint.json file
If you have added al these rules then your .eslint.json
should look like this
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"standard-with-typescript",
"plugin:react/recommended",
"next/core-web-vitals",
"plugin:@typescript-eslint/recommended",
"plugin:jsx-a11y/recommended",
"airbnb",
"airbnb-typescript",
"prettier"
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
"react/react-in-jsx-scope": [
"off"
],
"@typescript-eslint/indent": [
"warn",
4
],
"import/extensions": [
"error",
"never",
{
"scss": "always",
"svg": "always",
"json": "always"
}
],
"max-len": ["error",
{
"code": 120,
"ignoreUrls": true,
"ignoreRegExpLiterals": true
}],
"no-console": [
"error",
{
"allow": [
"error"
]
}
],
"comma-dangle": [
"warn",
{
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "never"
}
],
"@typescript-eslint/comma-dangle": [
"warn",
{
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "never"
}
],
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-unused-vars-experimental": "off",
"@typescript-eslint/naming-convention": "off",
"react/display-name": "off",
"react/require-default-props": "off",
"react/function-component-definition": [
"error",
{
"namedComponents": "arrow-function",
"unnamedComponents": "arrow-function"
}
],
"@typescript-eslint/explicit-function-return-type": "off"
}
}
Examples of errors
I am using the VS code extension error lens to show the errors on screen but here are what some of them will look like
Setting up Husky and lint-staged
Now that we have ESLint and prettier working with VS Code and in our project, it is time to make sure that these standards are kept especially if someone is using a different IDE or if they don't have the prettier or ESLint extensions installed.
This is where Husky and lint staged come in. When you commit your changes husky kicks in and runs any scripts you want, in this stage it is lint-staged. Lint-staged will run all the formatters on your staged files only. This makes sure that any files match your standards before they are committed but you will also need to make sure all your existing files are fixed before you implement this.
This is the order in which things happen
You commit your changes
git commit -m <Message>
This invokes husky which runs the pre commit script which in this case is lint-staged
Lint-staged will run ESLint and prettier it will try to fix the code
Your changes are committed if it could fix everything otherwise it fails and nothing is committed
Install and Setup
To install both packaged simple run:
npm i -D husky lint-staged
Now we need to initialise husky by running:
npx husky-init
This creates a few files and folders in your project
now we start by adding a prepare script to the package.json
file
npm pkg set scripts.prepare="husky install"
npm run prepare
Currently you husky pre-commit file only has the following code in it
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm test
So now we need to add our lint staged command so that it will run and we do this by typing the following command in your terminal
npx husky add .husky/pre-commit "npx lint-staged"
As we don't have any tests set up yet you need to remove that line so now your pre-commit file looks like
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
You could always have just added the line instead of the above command as well ๐
Now that all the husky setup is done we need to finish adding the script for lint-staged into a new file called .lintstagedrc.json
in the root of your project to this we add:
{
"**/*.{js, jsx,ts,tsx}": [
"npm run lint src"
],
"**/*.{json,js,ts,jsx,tsx,html}": [
"npx prettier"
]
}
in the above code this will not fix any issues but will instead report any issues it finds and if it finds any it will fail the commit.
If we try making a small edit to the testing file and then try to commit this file it will throw the following error
./app/components/testing.tsx
Error: This rule requires the `strictNullChecks` compiler option to be turned on to function correctly. @typescript-eslint/prefer-nullish-coalescing
Error: This rule requires the `strictNullChecks` compiler option to be turned on to function correctly. @typescript-eslint/strict-boolean-expressions
5:1 Warning: Expected indentation of 4 spaces but found 2. @typescript-eslint/indent
9:10 Warning: Missing trailing comma. comma-dangle
9:10 Warning: Missing trailing comma. @typescript-eslint/comma-dangle
10:1 Warning: Expected indentation of 4 spaces but found 8. @typescript-eslint/indent
13:1 Warning: Expected indentation of 4 spaces but found 8. @typescript-eslint/indent
13:9 Error: Unexpected console statement. no-console
15:1 Warning: Expected indentation of 4 spaces but found 2. @typescript-eslint/indent
16:1 Warning: Expected indentation of 8 spaces but found 4. @typescript-eslint/indent
17:1 Warning: Expected indentation of 4 spaces but found 2. @typescript-eslint/indent
./app/layout.tsx
Error: This rule requires the `strictNullChecks` compiler option to be turned on to function correctly. @typescript-eslint/prefer-nullish-coalescing
Error: This rule requires the `strictNullChecks` compiler option to be turned on to function correctly. @typescript-eslint/strict-boolean-expressions
8:1 Warning: Expected indentation of 4 spaces but found 2. @typescript-eslint/indent
9:1 Warning: Expected indentation of 4 spaces but found 2. @typescript-eslint/indent
12:16 Error: Function component is not an arrow function react/function-component-definition
13:1 Warning: Expected indentation of 4 spaces but found 2. @typescript-eslint/indent
15:1 Warning: Expected indentation of 4 spaces but found 2. @typescript-eslint/indent
17:1 Warning: Expected indentation of 4 spaces but found 2. @typescript-eslint/indent
18:1 Warning: Expected indentation of 8 spaces but found 4. @typescript-eslint/indent
19:1 Warning: Expected indentation of 12 spaces but found 6. @typescript-eslint/indent
20:1 Warning: Expected indentation of 8 spaces but found 4. @typescript-eslint/indent
21:1 Warning: Expected indentation of 4 spaces but found 2. @typescript-eslint/indent
./app/page.tsx
Error: This rule requires the `strictNullChecks` compiler option to be turned on to function correctly. @typescript-eslint/prefer-nullish-coalescing
Error: This rule requires the `strictNullChecks` compiler option to be turned on to function correctly. @typescript-eslint/strict-boolean-expressions
3:16 Error: Function component is not an arrow function react/function-component-definition
6:1 Warning: Expected indentation of 4 spaces but found 2. @typescript-eslint/indent
11:1 Error: This line has a length of 313. Maximum allowed is 120. max-len
15:1 Error: This line has a length of 207. Maximum allowed is 120. max-len
35:1 Error: This line has a length of 639. Maximum allowed is 120. max-len
At this stage you could either leave it like that so that you need to manually fix any errors that appear or you can add --fix
so that it will try and fix any errors it comes across Also you can add --write
to prettier so your .lintstagedrc.json
will now look like
{
"**/*.{js,jsx,ts,tsx}": [
"npm run lint src --fix"
],
"**/*.{json,js,ts,jsx,tsx,html}": [
"npx prettier --write"
]
}
GitHub Repo
All these files can be found on my GitHub Repo
Subscribe to my newsletter
Read articles from Steven Burrows directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by