If your team has ever left a comment like "missing semicolon" or "wrong quote style" on a pull request, this post is for you.
Formatting is not a code review topic. It should be handled automatically before the code even reaches a PR. Here is how to do that cleanly using Husky and lint-staged.
What are we actually solving?
The problem is simple: developers format code differently, editors have different defaults, and without enforcement, you end up with inconsistent style that creates noisy diffs and unnecessary review comments.
The goal is to auto-format and auto-lint staged files right before every commit, so bad formatting never reaches the repo.
Two tools make this easy:
- Prettier handles formatting (spacing, quotes, semicolons, line length)
- ESLint handles code quality (unused variables, missing dependencies, bad patterns)
- Husky lets you run scripts automatically on git events like pre-commit
- lint-staged runs those scripts only on staged files, not your entire codebase
Running tools on the full project on every commit would be slow. lint-staged keeps it fast by scoping to only what you changed.
Step 1: Install the tools
npm install --save-dev husky lint-staged
If you do not already have Prettier and ESLint set up:
npm install --save-dev prettier eslint eslint-config-prettier eslint-plugin-prettier
eslint-config-prettier disables ESLint rules that conflict with Prettier. eslint-plugin-prettier makes ESLint report Prettier violations as lint errors. Without these two, ESLint and Prettier will fight each other.
Step 2: Initialize Husky
npx husky init
This creates a .husky/ folder and adds a prepare script to your package.json:
"scripts": {
"prepare": "husky"
}
The prepare script runs automatically on npm install. That means anyone who clones your repo and installs dependencies gets the pre-commit hook set up without doing anything manually. This is the key to making it work across a team.
Step 3: Set up the pre-commit hook
Open .husky/pre-commit and replace whatever is in it with:
npx lint-staged
That is all. Every time you run git commit, Husky will run lint-staged before the commit goes through.
Step 4: Configure lint-staged
Add this to your package.json:
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx}": "eslint --fix",
"src/**/*.{css,json,md}": "prettier --write"
}
This says: on staged JS and JSX files inside src/, run ESLint with auto-fix. On staged CSS, JSON, and Markdown files, run Prettier.
Why split them? Because eslint-plugin-prettier already runs Prettier internally when you run eslint --fix on JS files, so you do not need to run both. For CSS and other file types, Prettier handles it directly since ESLint does not cover them.
If you are working on a plain HTML or CSS project without ESLint, simplify to just:
"lint-staged": {
"**/*.{html,css,js,json,md}": "prettier --write"
}
Step 5: Add a .prettierignore file
Create .prettierignore in your project root:
dist/
node_modules/
This prevents Prettier from formatting compiled or generated output. Without this, it might reformat your minified CSS or bundled JS, which is not what you want.
How it works in practice
You make changes to a few files, stage them with git add, and run git commit. Husky fires the pre-commit hook. lint-staged checks which staged files match your patterns and runs the tools only on those. If ESLint finds an error it cannot auto-fix, the commit is blocked and you see the error. If everything passes, the commit goes through with clean, formatted code.
The first time you set this up, you will probably want to run a full format pass on the whole project so everything starts from a clean state:
npx prettier --write "src/**/*.{js,jsx,css,json}"
Commit that as a single formatting commit, then the pre-commit hook keeps things clean going forward.
VS Code integration (the finishing touch)
The pre-commit hook is your safety net. But you can also make formatting happen as you type by adding a .vscode/settings.json to your project:
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "always"
},
"eslint.validate": ["javascript", "javascriptreact"],
"files.autoSave": "off"
}
With this, every time you hit save, VS Code runs Prettier and ESLint automatically. By the time you stage your files, they are already formatted. The Husky hook just confirms nothing slipped through.
Commit this file to the repo so the whole team gets the same editor behavior without any manual setup.
The result
- No more formatting comments in code reviews
- Clean diffs that show only real changes
- Commits that are always consistent, regardless of who made them
- New team members get the full setup just by running
npm install
It takes about ten minutes to set up and saves hours of back-and-forth over style issues. Worth it.
Top comments (0)