Complete Guide on Setting Up ESLint & Prettier to Work With Next.js-Typescript Project (create-next-app --ts)
I started a new project with Next.js and wanted to get eslint and prettier to work with it, but the overall process turned out to be harder than I anticipated. So I decided to document the steps that I followed to help out the next person facing the same issue.
Issue
create-next-app doesn't come with eslint set up out-of-the-box to run automatically, so you need to set it up manually. Since next encapsulates all the logic for running the dev server, we need to make some modifications to the next.config.js file to get eslint to run while the dev server is running. For anyone just starting out with Next.js it can be an overwhelming experience given how next is slightly different from a basic create-react-app. As part of this guide we'll also be creating a pre-commit hook using husky & lint-staged for running eslint before we commit our code.
Creating a Next.js app (create-next-app)
Let's start by creating a new application. If you've already created one then feel free to skip this step.
It's pretty easy to get going. Just run the command posted on the Next.js getting started page.
npx create-next-app --ts
Note that here we're using the --ts
option to get started with a typescript project.
Once this is complete, you should have an .eslintrc.json
file along with a lint script in your package.json
which you can execute by running npm run lint
.
Installing ESLint plugins & Prettier
Now let's install some plugins for eslint that we'll use to get eslint working nicely with typescript & prettier.
Install typescript-eslint/eslint-plugin
We'll speify this plugin in our .eslintrc.json
file
npm install --save-dev @typescript-eslint/eslint-plugin
Let's update the .eslintrc.json
file and add this plugin.
// .eslintrc.json
"plugins": ["@typescript-eslint"], // Add this line
"extends": "next/core-web-vitals"
Adding Prettier
Let's add prettier to our project and define some basic rule in .prettierrc
file.
npm install --save-dev prettier
Now we'll create a basic .prettierrc
file
// .prettierrc
{
"semi": false,
"trailingComma": "es5",
"singleQuote": true,
"tabWidth": 2,
"useTabs": false
}
Feel free to make any modifications based on your preference. Also, at this point you should set up your IDE/TextEditor to work with prettier and automatically format the document on save. This will make development much faster and make your life 1000 times better.
Make sure prettier is working and formatting your document as expected before proceeding. You might have to restart your IDE/TextEditor for the changes to take effect
ESLint With Prettier
Let's setup eslint to work with prettier and show prettier errors when linting.
npm install --save-dev eslint-config-prettier
Now let's add some extensions to our .eslintrc.json
file
// .eslintrc.json
{
"plugins": ["@typescript-eslint"],
"extends": [
"next",
"next/core-web-vitals",
"plugin:@typescript-eslint/recommended",
"prettier" // Add "prettier" last. This will turn off eslint rules conflicting with prettier. This is not what will format our code.
]
}
Let's make sure what we have so far is working. Let's define an unused variable in the index.tsx file const errorCausingVar = 'string'
and then run npm run lint
in your terminal. If everything works then you should see a warning that looks something like this
./pages/index.tsx
6:7 Warning: 'errorCausingVar' is assigned a value but never used. @typescript-eslint/no-unused-vars
Running Lint on Dev Server
Now we need to make sure that the next dev
script runs the linter so that we can see the issues in terminal as and when you make changes to the file with the dev server running. You might see online that some places recommend using eslint-loader
with webpack but since that library has been deprecated, we'll use the new eslint-webpack-plugin
library instead.
Installing eslint-webpack-plugin
Let's install the plugin to use with webpack
npm install --save-dev eslint-webpack-plugin
Adding Plugin to Webpack
Now we'll update the webpack config using the next.config.js file. For more details on how this file works you should checkout Next.js official documentation
// next.config.js
/* eslint-disable */
const ESLintPlugin = require('eslint-webpack-plugin')
/** @type {import('next').NextConfig} */
module.exports = {
reactStrictMode: true,
webpack: (config, { dev }) => {
if (dev) { // Add this plugin only in dev mode
config.plugins.push(
new ESLintPlugin({
context: './', // Location where it will scan all the files
extensions: ['js', 'jsx', 'ts', 'tsx'], // File formats that should be scanned
exclude: ['node_modules', 'dist'], // Exclude everything in these folders
})
)
}
return config
},
}
Here we're adding a new instance of the eslint-webpack-plugin
plugin to the webpack config and setting it up to scan all the .js
, .ts
, .jsx
, and .tsx
files except for those inside node_modules
and dist
folders.
Now let's make sure everything is working. Leave the unused variable that we added earlier in index.js
and run the dev server npm run dev
. Now go to the browser and hit localhost:3000
(the URL of your dev server) and then check the terminal, you should see a warning about the unused variable
/pages/index.tsx
6:7 warning 'errorCausingVar' is assigned a value but never used @typescript-eslint/no-unused-vars
At this point you can start modifying .eslintrc.json
as per your preference and it should work as expected. I like to add the following rules to mine
// .eslintrc.json
{
"plugins": ["@typescript-eslint"],
"extends": [
"next",
"next/core-web-vitals",
"plugin:@typescript-eslint/recommended",
"prettier" // Add "prettier" last. This will turn off eslint rules conflicting with prettier. This is not what will format our code.
],
"rules": { // Rules that I add manually.
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-explicit-any": "error"
}
}
Now that we have everything working, the last step would be to add a hook which will run the linter on git commit
to ensure that we don't end up pushing lint errors to our repository.
Adding husky & lint-staged to check for errors on git commit
Even though we've added lint check to our dev server, let's add another layer of check to ensure that we don't end up pushing lint errors to our remote repository.
Adding husky
Husky is a tool that allows us to create hooks which can run what we tell it on a git event. We're going to use the pre-commit hook and run our linter.
npm install --save-dev husky
Now let's install husky's hooks.
npx husky install
Now let's add a pre-commit hook that will run lint-staged (we'll install set it up in the next step)
npx husky add .husky/pre-commit "npx lint-staged"
You show see this in your terminal
husky - created .husky/pre-commit
And you should see a per-commit
file that looks like this in .husky
folder
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
Adding lint-staged
Lint staged is a tool that will run eslint on staged files. This will speed up the commit process by reducing the files that needs to be checked on every commit.
npm install --save-dev lint-staged
Now let's configure lint-staged
in our package.json
{
/*
... rest of the package.json
*/
// Add the following lines
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix"
]
}
}
Here we're telling lint-staged
to run eslint --fix
on the following file types .js
, .ts
, .jsx
, and .tsx
.
Now, with the unused variable still in index.js
, let's try to commit our changes
git add --all
git commit -m "Test commit to see if husky & lint-staged works"
Now your should see something like this in your terminal
✔ Preparing...
⚠ Running tasks...
❯ Running tasks for *.{js,jsx,ts,tsx}
✖ eslint --fix [FAILED]
↓ Skipped because of errors from tasks. [SKIPPED]
✔ Reverting to original state because of errors...
✔ Cleaning up...
Followed by
/pages/index.tsx
6:7 error 'errorCausingVar' is assigned a value but never used @typescript-eslint/no-unused-vars
✖ 1 problem (1 error, 0 warnings)
husky - pre-commit hook exited with code 1 (error)
Note: You can skip this check on commit by adding --no-verify
to your git commit command. Eg. git commit -m "skip lint check" --no-verify
Voila! We have completed setting up eslint to work with our Next.js project! 🎉
Here's a boilerplate that I created with eslint & prettier (as described above), docker, and nginx set up with a basic create-next-app. Feel free to check it out and provide feedback! :)