Configure ESLint, Prettier, Husky, lint-staged Properly for Next.js

How to Use ESLint, Prettier, Husky, and lint-staged with Next.js for Git Hooks

If you choosed TypeScript and ESLint during Next.js CLI, then this article is for you to setup Prettier, Husky, lint-staged properly to proceed ahead and great project structure. If you work with other developer or want to maintain the project in future, then this is a must-do for Next.js project

Packages Install

npm install -D eslint eslint-config-next eslint-config-prettier eslint-plugin-prettier prettier prettier-plugin-tailwindcss eslint-plugin-tailwindcss @typescript-eslint/eslint-plugin @typescript-eslint/parser husky lint-staged
  • eslint: The pluggable linting utility for JavaScript and JSX. ESLint is an open source project that helps you find and fix problems with your JavaScript code.

  • eslint-config-next: This package provides an ESLint configuration optimized for Next.js projects, ensuring adherence to Next.js best practices and conventions.

  • eslint-config-prettier: Disables ESLint rules that conflict with Prettier formatting, allowing Prettier to handle all code style concerns without interference.

  • eslint-plugin-prettier: Integrates Prettier into ESLint as a rule, enabling code to be automatically formatted by Prettier whenever ESLint runs.

  • prettier: A code formatter that enforces a consistent style by parsing and reformatting code according to predefined rules, ensuring readability across different projects.

  • prettier-plugin-tailwindcss: A Prettier plugin that automatically organizes Tailwind CSS class names in a consistent order according to Tailwind's recommendation.

  • eslint-plugin-tailwindcss: A ESLint plugin that automatically organizes Tailwind CSS class names in a consistent order according to Tailwind's recommendation and many more.

  • @typescript-eslint/eslint-plugin: Provides a set of ESLint rules specific to TypeScript, helping to catch and enforce best practices in TypeScript codebases.

  • @typescript-eslint/parser: An ESLint parser that allows ESLint to parse TypeScript code, enabling linting for TypeScript files.

  • husky: A tool that enables Git hooks, allowing commands like linting, testing, or formatting to run automatically at key stages of Git workflows, such as before committing.

  • lint-staged: A tool that runs linters or formatters on only the staged files in Git, ensuring that only the modified files are processed before a commit.

ESLint Config File

ESLint version > 9.0 support eslint.config.js, So eslint.config.mjs:

import prettier from "eslint-plugin-prettier";
import tailwindcss from "eslint-plugin-tailwindcss";
import globals from "globals";
import tsParser from "@typescript-eslint/parser";
import path from "node:path";
import { fileURLToPath } from "node:url";
import js from "@eslint/js";
import { FlatCompat } from "@eslint/eslintrc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
  baseDirectory: __dirname,
  recommendedConfig: js.configs.recommended,
  allConfig: js.configs.all,
});

const eslintConfig = [
  ...compat.extends(
    "next/core-web-vitals",
    "next/typescript",
    "eslint:recommended",
    "plugin:@next/next/recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:prettier/recommended",
    "plugin:tailwindcss/recommended",
  ),
  {
    plugins: {
      prettier,
      tailwindcss,
    },
    languageOptions: {
      globals: {
        ...globals.browser,
        ...globals.node,
      },
      parser: tsParser,
      ecmaVersion: 12,
      sourceType: "module",
      parserOptions: {
        ecmaFeatures: {
          jsx: true,
        },
      },
    },

    rules: {
      "@typescript-eslint/no-explicit-any": "off",
      "react/react-in-jsx-scope": "off",
      "prettier/prettier": [
        "error",
        {
          endOfLine: "auto",
        },
      ],
    },
  },
];

export default eslintConfig;

Previous: eslintrc.json:

{
  "env": {
    "browser": true,
    "es2021": true
  },
  "extends": [
    "next/core-web-vitals",
    "next/typescript",
    "eslint:recommended",
    "plugin:@next/next/recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:prettier/recommended",
    "plugin:tailwindcss/recommended"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": 12,
    "sourceType": "module"
  },
  "plugins": [
    "prettier"
  ],
  "rules": {
    "@typescript-eslint/no-explicit-any": "off",
    "prettier/prettier": [
      "error",
      {
        "endOfLine": "auto"
      }
    ],
    "react/react-in-jsx-scope": "off"
  }
}

Prettier Config File

Newer: prettier.config.mjs:

// prettier.config.js, .prettierrc.js, prettier.config.mjs, or .prettierrc.mjs

/**
 * @see https://prettier.io/docs/en/configuration.html
 * @type {import("prettier").Config}
 */
const config = {
  trailingComma: "es5",
  tabWidth: 2,
  semi: true,
  singleQuote: false,
  bracketSpacing: true,
  arrowParens: "always",
  jsxSingleQuote: false,
  bracketSameLine: false,
  endOfLine: "lf",
  plugins: ["prettier-plugin-tailwindcss"],
  printWidth: 80,
  experimentalTernaries: false,
  tailwindConfig: "tailwind.config.ts",
  tailwindEntryPoint: "tailwind.config.ts",
  quoteProps: "as-needed",
  proseWrap: "always",
  htmlWhitespaceSensitivity: "css",
  embeddedLanguageFormatting: "auto",
  useTabs: false,
  requirePragma: false,  
  insertPragma: false,
  vueIndentScriptAndStyle: false,
  singleAttributePerLine: true,
};

export default config;

prettierrc.json:

{
  "printWidth": 80,
  "useTabs": false,
  "tabWidth": 2,
  "trailingComma": "es5",
  "semi": true,
  "singleQuote": false,
  "bracketSpacing": true,
  "arrowParens": "always",
  "jsxSingleQuote": false,
  "bracketSameLine": false,
  "endOfLine": "lf",
  "plugins": ["prettier-plugin-tailwindcss"]
}

Husky Configurations

Husky has been installed before during installations.

npx husky init

It creates a .husky/ folder where the Git hooks will reside and the .husky folder contains a file named “pre-commit“.

This will also add a new script in your package.json file:

  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "prepare": "husky",
  },

If not added, then please manually add this:

"prepare": "husky",

Add Pre-Commit Hooks:

Add necessary commands to this .husky/pre-commit file.

npx lint-staged

This will configure Husky.

Lint-staged Configurations

Next, configure lint-staged in your package.json same block as scripts to specify that ESLint and Prettier should run only on staged files. Add the following configuration:

"lint-staged": {
  "*.{js,jsx,ts,tsx}": [
    "eslint --fix './src/**/*.{js,jsx,ts,tsx}'",
    "prettier --write './src/**/*.{js,jsx,ts,tsx,css}'"
  ]
}

This configuration ensures that ESLint will fix any linting issues, and Prettier will format files before they are committed.

Update package.json Scripts

Add these to your scripts list of package.json:

"format": "prettier --check './src/**/*.{js,jsx,ts,tsx,css}'",
"format:fix": "prettier --write './src/**/*.{js,jsx,ts,tsx,css}'",
"lint:fix": "eslint --fix './src/**/*.{js,jsx,ts,tsx}'"

Now the script section of package.json will look like:

  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "prepare": "husky",
    "format": "prettier --check './src/**/*.{js,jsx,ts,tsx,css}'",
    "format:fix": "prettier --write './src/**/*.{js,jsx,ts,tsx,css}'",
    "lint:fix": "eslint --fix './src/**/*.{js,jsx,ts,tsx}'"
  },

Conclusion

This article offers a detailed setup guide for integrating Prettier, Husky, and lint-staged into a Next.js project using TypeScript and ESLint. It provides step-by-step instructions on installing the required packages, configuring ESLint and Prettier, setting up Husky for Git hooks, and configuring lint-staged to maintain code quality before committing changes. Furthermore, it updates project scripts for formatting and linting, ensuring a consistent and maintainable codebase when collaborating with other developers.