DEV Community

Cover image for Setup Husky, EsLint and Prettier in React + Vite Projects
Navdeep Mishra
Navdeep Mishra

Posted on

Setup Husky, EsLint and Prettier in React + Vite Projects

Whether you're a senior developer or just starting out, writing clean, consistent code isn't optional — it's the baseline. The good news is you don't have to enforce it by hand. In this guide, we'll wire up Prettier, ESLint, Husky, and lint-staged so your codebase enforces its own standards automatically, on every single commit.

What we're building

Tool Job
ESLint Catches bugs and enforces code-quality rules
Prettier Formats code consistently — no more style debates
Husky Runs scripts automatically at git hook points (e.g. pre-commit)
lint-staged Runs linters/formatters only on the files you've staged, not the whole repo

By the end, every commit will be automatically linted and formatted — and bad commits won't get through.

Prerequisites

  • [ ] Node.js 18.18+ (Node 20+ recommended)
  • [ ] A React project already set up with Vite + ESLint
  • [ ] A git repository (git init if you haven't already — Husky needs this) We'll use Husky v9's simplified init flow, which is the current standard as of 2026.

In this guide

  1. Scaffold the project
  2. Install and configure Prettier
  3. Add a formatting script
  4. Stop ESLint and Prettier from conflicting
  5. Install and initialize Husky
  6. Add lint-staged
  7. Test the full setup
  8. (Optional) VS Code integration
  9. (Optional) Enforce commit message format

1. Scaffold the project

Start a new project using Vite, choosing React with ESLint and TypeScript (recommended):

yarn create vite
Enter fullscreen mode Exit fullscreen mode

Just follow the on-screen prompts — that's all it takes.


2. Install and configure Prettier

yarn add -D prettier
Enter fullscreen mode Exit fullscreen mode

Create a .prettierrc in your project root:

{
  "semi": true,
  "singleQuote": true,
  "trailingComma": "es5",
  "printWidth": 80,
  "tabWidth": 2,
  "arrowParens": "always"
}
Enter fullscreen mode Exit fullscreen mode

💡 Adjust these to your team's taste — they're just the most commonly used defaults.

Then create a .prettierignore next to it:

dist
build
node_modules
coverage
package-lock.json
pnpm-lock.yaml
yarn.lock
Enter fullscreen mode Exit fullscreen mode

3. Add a formatting script

Your package.json likely already has a lint script from the Vite template. Add formatting scripts alongside it:

{
  "scripts": {
    "format": "prettier --write .",
    "format:check": "prettier --check ."
  }
}
Enter fullscreen mode Exit fullscreen mode

Test it:

yarn format:check
Enter fullscreen mode Exit fullscreen mode

4. Stop ESLint and Prettier from conflicting

Some ESLint stylistic rules (spacing, semicolons, quotes) can disagree with Prettier's output. Rather than fighting rule-by-rule, install eslint-config-prettier, which simply turns off any ESLint rule that would conflict with Prettier:

yarn add -D eslint-config-prettier
Enter fullscreen mode Exit fullscreen mode

Then wire it into your eslint.config.js:

import js from '@eslint/js';
import globals from 'globals';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import tseslint from 'typescript-eslint';
import { defineConfig, globalIgnores } from 'eslint/config';
import eslintConfigPrettier from 'eslint-config-prettier';

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      js.configs.recommended,
      tseslint.configs.recommended,
      reactHooks.configs.flat.recommended,
      reactRefresh.configs.vite,
      eslintConfigPrettier, // 👈 must come last
    ],
    languageOptions: {
      globals: globals.browser,
    },
  },
]);
Enter fullscreen mode Exit fullscreen mode

5. Install and initialize Husky

yarn add -D husky
yarn husky init
Enter fullscreen mode Exit fullscreen mode

husky init does two things automatically:

  • Creates a .husky/ directory with a sample pre-commit hook
  • Adds a prepare script to package.json:
{
  "scripts": {
    "prepare": "husky"
  }
}

The prepare script runs automatically after npm install, so every teammate who clones the repo gets the git hooks set up — no manual step needed on their end.

ℹ️ Husky v9 hook files are plain shell scripts with no shebang line needed. (Older guides showing #!/usr/bin/env sh at the top are for Husky v8 and earlier.)


6. Add lint-staged

Running ESLint and Prettier across the entire project on every commit gets slow as the codebase grows. lint-staged runs them only on the files you've actually staged.

yarn add -D lint-staged
Enter fullscreen mode Exit fullscreen mode

Add a lint-staged block to package.json:

{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,css,md,html}": [
      "prettier --write"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Now replace the contents of .husky/pre-commit with:

npx lint-staged
Enter fullscreen mode Exit fullscreen mode

That's it — no shebang, no sourcing a helper script.


7. Test the full setup

Intentionally mess up a file's formatting (extra spaces, wrong quotes, a missing semicolon), then stage and commit it:

git add .
git commit -m "test: verify pre-commit hooks"
Enter fullscreen mode Exit fullscreen mode

You should see lint-staged run, auto-fix the formatting, and re-stage the file before the commit completes. If there's a real lint error (not just a style issue), the commit will be blocked until you fix it.

⚠️ Emergency bypass (not recommended as a habit):

git commit -m "message" --no-verify

8. (Optional) VS Code integration

Install the ESLint and Prettier VS Code extensions, then add .vscode/settings.json:

{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  }
}
Enter fullscreen mode Exit fullscreen mode

This formats on save and auto-fixes lintable issues as you work, so the pre-commit hook rarely has anything left to do.


9. (Optional) Enforce commit message format

If you also want conventional commit messages enforced:

yarn add -D @commitlint/cli @commitlint/config-conventional
Enter fullscreen mode Exit fullscreen mode

Create commitlint.config.js:

export default { extends: ['@commitlint/config-conventional'] };
Enter fullscreen mode Exit fullscreen mode

Create .husky/commit-msg:

npx --no -- commitlint --edit "$1"
Enter fullscreen mode Exit fullscreen mode

Now a commit message like asdf fix stuff gets rejected in favor of something like fix: correct button alignment.


Wrapping up

With this setup, every commit is automatically formatted, linted, and (optionally) checked for a proper message — before it ever leaves your machine. No more "please run Prettier before pushing" comments in code review.

Recap:

  • ✅ Prettier formats your code consistently
  • eslint-config-prettier keeps ESLint and Prettier from fighting
  • ✅ Husky runs hooks automatically on every clone, thanks to prepare
  • ✅ lint-staged keeps checks fast by only touching staged files
  • ✅ (Optional) VS Code auto-fixes on save
  • ✅ (Optional) Commitlint enforces a readable commit history

If you found this useful, let me know in the comments — happy committing! 🚀

Top comments (0)