logo
    HomeWorksBlogs

    2025 Dan Varela. All Rights Reserved.

    View all blogs

    How to set up Prettier, Eslint, Lint-staged, and Husky on a Next.js project.

    Published August 24, 2024 (10 months ago) 3 min read

    Next.js
    Programming

    There are two things to consider when writing code:

    1. It should be correct.
    2. It should be readable.

    Code correctness is essential. Unless you intentionally make your code unreadable, readability is equally important—especially if you are not the only one contributing to the codebase.

    This is where formatters and linters become essential tools.

    • Linters help you catch errors in your code as you write it.
    • Formatters format the code you write according to a predefined set of conventions or guidelines ensuring its readability.

    In this guide, I will discuss how to set up Prettier (formatter) and ESLint (linter) in a Next.js project. I will also cover how to run your formatters and linters before you commit your code to Git.

    Let's start!

    Creating a new Next.js project

    First, let's create a new Next.js project. I will be using pnpm, but you can use your preferred package manager.

    sh

    1pnpm create next-app@latest

    Select 'Yes' when prompted to use ESLint. This will automatically set up ESLint for you. The rest of the options are up to your preference.

    Creating a next.js project
    Creating a next.js project

    Setting up Prettier

    Let's add Prettier to our project. To install, run the following command in your terminal:

    sh

    1pnpm add --save-dev --save-exact prettier

    We usually exclude dependency and build output files because we only want to format the code we maintain.

    To skip formatting these files, add a .prettierignore file with the following contents in your root directory:

    1/node_modules
    2/.pnp
    3.pnp.js
    4
    5# testing
    6/coverage
    7
    8# next.js
    9/.next/
    10/out/
    11
    12# production
    13/build
    14
    15# vercel
    16.vercel
    17
    18# typescript
    19next-env.d.ts

    Prettier has its defaults, but we can configure it according to our preference. Add a .prettierrc file in the root directory of your project and add your preferred configurations. In my case, I have this:

    json

    1{
    2  "endOfLine": "lf",
    3  "semi": false,
    4  "singleQuote": false,
    5  "tabWidth": 2,
    6  "trailingComma": "es5",
    7}
    8

    Now, we can format all the supported files in our codebase by running the following command:

    sh

    1pnpm exec prettier . --write

    Add this command into the package.json scripts:

    json

    1{
    2...
    3  "scripts": {
    4    "dev": "next dev",
    5    "build": "next build",
    6    "start": "next start",
    7    "lint": "next lint",
    8    "format": "pnpm exec prettier . --write"
    9  },
    10...
    11}
    12

    Now, we can run the following command to format the files:

    sh

    1pnpm format

    Setting up ESLint

    Next.js has already set up and configured ESLint for us when we created the project. However, ESLint has some rules that conflict with Prettier so we have to add a plugin that resolves these conflicts.

    Install eslint-config-prettier:

    sh

    1pnpm add --save-dev eslint-config-prettier

    Next, add prettier to our eslintrc.json file:

    json

    1{
    2  "extends": ["next/core-web-vitals", "prettier"]
    3}
    4

    Setting up lint-staged

    lint-staged is a tool we can use to run operations, such as formatting and linting, on staged git files. To install, run the following command:

    sh

    1pnpm install --save-dev lint-staged

    We need to specify the operations we want lint-staged to run on the staged Git files. To do this, add a lintstagedrc.mjs file at the root directory of your project and add the following contents:

    javascript

    1import path from "path";
    2
    3const buildEslintCommand = (filenames) =>
    4  `next lint --file ${filenames
    5    .map((f) => path.relative(process.cwd(), f))
    6    .join(" --file ")}`;
    7
    8export default {
    9  "**/*.ts?(x)": [
    10    "prettier --write",
    11    () => "tsc -p tsconfig.json --noEmit",
    12    buildEslintCommand,
    13  ],
    14  "**/*.{js,jsx}": ["prettier --write", buildEslintCommand],
    15};
    16

    When we run lint-staged using the following command:

    sh

    1pnpm exec lint-staged
    • When lint-staged encounters *.ts or *.tsx files, it:
      • Formats them.
      • Checks for type errors.
      • Lints them in the context of a Next.js project.
    • The same happens on *.js or *.jsx files but it skips the type checking.

    Automatically running lint-staged before commit

    Manually running lint-staged every time we commit our code can be quite annoying, and there's a chance we might forget to run it. Husky is a tool that automates this process.

    First, we install it:

    sh

    1pnpm add --save-dev husky

    Then, run the script, which will automatically set up Husky in our project:

    sh

    1pnpm exec husky init

    In the .husky/pre-commit file, add the following so that Husky runs lint-staged when committing our code.

    1npx lint-staged

    Testing

    Let's try adding a code with error:

    typescript

    1// src/app/test.tsx
    2
    3// Let's import a library that does not exist
    4import something from "something"
    5

    When we try to add and commit the code, we should get a nice error preventing us from doing so:

    Pre commit hook error

    This time, let's replace our code with a correct but unformatted one:

    typescript

    1// src/app/test.tsx
    2const person = {name: "John Doe", email: "[email protected]", description: "A really long string so that this triggers the formatter to break this object out into multiple lines"}

    After adding and committing our code, it should be formatted. In my case, it looks something like this:

    typescript

    1// src/app/test.tsx
    2const person = {
    3  name: "John Doe",
    4  email: "[email protected]",
    5  description:
    6    "A really long string so that this triggers the formatter to break this object out into multiple lines",
    7};
    8

    Now, you can continue adding code to your project, knowing it will be formatted and error-checked whenever you commit.

    References

    • Prettier Documentation
    • lint-staged Documentation
    • Husky Documentation