Apr 19, 2025

How to Set Up ESLint + Prettier + Husky + Lint-Staged in a Next.js Project

Go back
How to Set Up ESLint + Prettier + Husky + Lint-Staged in a Next.js Project

How to Set Up ESLint + Prettier + Husky + Lint-Staged in a Next.js Project

In this blog post, I’ll walk you through how to set up a powerful developer workflow using ESLintPrettierHusky, and Lint-Staged in a modern Next.js + TypeScript project.

✅ Goal: Ensure clean, consistent code formatting, automatic linting before commit, and enforceable team-wide coding standards.


1. Install Dependencies

If you're using TypeScript and React:

npm install -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-simple-import-sort eslint-config-prettier

2. Prettier Configuration

📄 .prettierrc

{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "all",
  "printWidth": 100,
  "bracketSpacing": true,
  "arrowParens": "always",
  "endOfLine": "lf",
  "overrides": [
    {
      "files": "*.json",
      "options": {
        "printWidth": 80
      }
    }
  ]
}

📄 .prettierignore

node_modules/
.next/
dist/
public/
coverage/
prisma/migrations/

3. ESLint with Flat Config

📄 eslint.config.js

import path from "node:path";
import { fileURLToPath } from "node:url";
import js from "@eslint/js";
import typescriptEslint from "@typescript-eslint/eslint-plugin";
import typescriptParser from "@typescript-eslint/parser";
import simpleImportSort from "eslint-plugin-simple-import-sort";
import { FlatCompat } from "@eslint/eslintrc";
import { fixupConfigRules, fixupPluginRules } from "@eslint/compat";

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,
});

export default [
  { ignores: ["node_modules/**", ".next/**", "dist/**", "public/**"] },
  ...fixupConfigRules(
    compat.extends(
      "eslint:recommended",
      "plugin:import/recommended",
      "plugin:import/typescript",
      "plugin:@typescript-eslint/recommended",
      "plugin:react/recommended",
      "plugin:react-hooks/recommended",
      "plugin:jsx-a11y/recommended",
      "plugin:@next/next/recommended"
    )
  ),
  {
    plugins: {
      "@typescript-eslint": fixupPluginRules(typescriptEslint),
      "simple-import-sort": simpleImportSort,
    },
    languageOptions: {
      parser: typescriptParser,
      parserOptions: {
        ecmaVersion: "latest",
        sourceType: "module",
        ecmaFeatures: { jsx: true },
      },
    },
    settings: { react: { version: "detect" } },
    rules: {
      "simple-import-sort/imports": ["error"],
      "simple-import-sort/exports": ["error"],
      "import/first": ["error"],
      "import/newline-after-import": ["error", { count: 1 }],
      "@typescript-eslint/no-explicit-any": "off",
      "@typescript-eslint/no-unused-vars": ["error", {
        argsIgnorePattern: "^_",
        varsIgnorePattern: "^_",
        caughtErrorsIgnorePattern: "^_"
      }],
      "no-negated-condition": "error"
    },
  }
];

4. Lint-Staged Configuration

📄 .lintstagedrc.mjs

import { relative } from "node:path";

function buildEslintCommand(filenames) {
  return `next lint --file ${filenames.map(f => relative(process.cwd(), f)).join(" --file ")}`;
}

export default {
  "*.{js,jsx,ts,tsx}": [
    "prettier --write",
    buildEslintCommand
  ],
  "*.{json,md,yml,yaml,html,css,scss}": ["prettier --write"]
};

5. Husky Setup

Add to package.json:

"scripts": {
  "prepare": "husky install"
}

Add a pre-commit hook:

npx husky add .husky/pre-commit "npx lint-staged"

📄 .husky/pre-commit

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged

6. Recommended VS Code Settings

📄 .vscode/settings.json

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll": "explicit"
  }
}

✅ Summary

ToolPurposeESLintCode quality & error detectionPrettierConsistent code formattingLint-StagedRun Prettier/ESLint only on staged filesHuskyGit hook to run checks before commits

By following this setup, your Next.js + TypeScript project will be clean, consistent, and ready for team collaboration. This configuration ensures all code is linted and formatted before every commit, saving time and reducing code review friction.

Need help extending this setup to include commit linting, CI/CD checks, or GitHub Actions? Drop a message — I'm happy to help!

Related.

Related.

Related.

Related.