Husky + lint-staged: Shared Hooks & Fast Linting
Husky = hooks ko repo mein store karo. lint-staged = sirf staged files lint karo. Fast, shared, effective.
In Chapter 8.1, we learned that .git/hooks/ is NOT tracked by Git.
Every developer must manually set up hooks after cloning. They WILL forget.
Without shared hooks: one developer has pre-commit linting, another doesn't. Inconsistent quality.
The solution: store hooks IN the repository (tracked by Git), and install them to .git/hooks/ automatically.
Husky does exactly this. It's the industry standard for sharing Git hooks in JavaScript/TypeScript projects.
Before Husky, teams used manual scripts or lefthook. Husky won because of simplicity and npm integration.
For non-JS projects: alternatives include lefthook (language-agnostic), pre-commit framework (Python).
# The problem without Husky:
# Developer A creates a pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
npm run lint
EOF
# This hook ONLY exists on Developer A's machine
# .git/ is not tracked by Git
# Developer B clones the repo
git clone https://github.com/team/project.git
cd project
# Developer B has NO pre-commit hook
ls .git/hooks/pre-commit
# No such file or directory
# Developer B commits with lint errors
echo "const x=1" > messy.js
git add messy.js
git commit -m "add messy code"
# ✅ Commit succeeds — NO linting!
# Bad code enters the repository!
# With Husky: Developer B runs npm install
# Husky automatically sets up .git/hooks/ from repo config
# Developer B has the SAME hooks as Developer AHusky stores hook definitions in your project directory (tracked by Git).
On npm install, Husky's prepare script runs and installs hooks to .git/hooks/.
Setup steps: install husky → initialize → add hooks → commit .husky/ directory.
Modern Husky (v9+): npx husky init creates .husky/ directory with a sample pre-commit hook.
The prepare script in package.json ensures hooks are installed on every npm install.
Hooks in .husky/ are just shell scripts — same as .git/hooks/ but tracked by Git.
Every team member who runs npm install gets the same hooks automatically.
# Step 1: Install Husky
npm install --save-dev husky
# Step 2: Initialize Husky (creates .husky/ directory)
npx husky init
# Creates:
# .husky/pre-commit (sample hook)
# Adds "prepare": "husky" to package.json
# Step 3: Check what was created
cat package.json | grep prepare
# "prepare": "husky"
ls .husky/
# pre-commit
cat .husky/pre-commit
# npm test
# Step 4: Customize the pre-commit hook
echo "npx lint-staged" > .husky/pre-commit
# Step 5: Commit .husky/ to the repo
git add .husky/ package.json package-lock.json
git commit -m "chore: add Husky hooks"
# Now every developer who clones and runs npm install
# will automatically get the pre-commit hook!The #1 problem with pre-commit linting: linting the ENTIRE project is slow.
lint-staged solves this: it runs linters ONLY on files that are staged for commit.
If you change 2 files, lint-staged runs ESLint on only those 2 files — instant.
lint-staged also AUTO-FIXES what it can (formatting, simple lint errors) and re-stages the fixed files.
Configuration goes in package.json or .lintstagedrc.json.
Common config: { "*.js": ["eslint --fix", "prettier --write"], "*.css": ["prettier --write"] }.
The pattern: Husky triggers pre-commit → lint-staged runs → only staged files are checked → fast!
This is the industry standard setup for JavaScript/TypeScript projects.
Step 1: Install both packages.
Step 2: Initialize Husky and create pre-commit hook that runs lint-staged.
Step 3: Configure lint-staged in package.json.
Step 4: Add commit-msg hook for commitlint (optional).
Step 5: Add pre-push hook for running tests (optional).
Step 6: Commit everything — team members get hooks automatically on npm install.
# Complete setup — copy this for any new project
# Step 1: Install
npm install --save-dev husky lint-staged prettier eslint
# Step 2: Initialize Husky
npx husky init
# Step 3: Create pre-commit hook with lint-staged
echo "npx lint-staged" > .husky/pre-commit
# Step 4: Configure lint-staged in package.json
# Add this to your package.json:
# "lint-staged": {
# "*.{js,ts}": ["eslint --fix", "prettier --write"],
# "*.{json,md,css,html}": ["prettier --write"]
# }
# Step 5: Optional — add commit-msg hook for commitlint
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
# Step 6: Optional — add pre-push hook for tests
echo "npm test" > .husky/pre-push
# Step 7: Commit everything
git add .husky/ package.json package-lock.json
git commit -m "chore: add Husky + lint-staged"
# Now your team workflow:
# 1. Developer clones repo
git clone https://github.com/team/project.git
cd project
# 2. npm install sets up hooks automatically
npm install
# husky - Git hooks installed
# 3. Developer commits — hooks run automatically!
echo "const x = 1" > app.js
git add app.js
git commit -m "feat: add app"
# ✔ Pre-commit hook runs lint-staged
# ✔ ESLint + Prettier run on app.js only
# ✔ Commit proceeds with clean codeConditional hooks: run different checks based on file type or branch.
Skipping hooks in CI: set HUSKY=0 environment variable to disable hooks in CI environments.
Monorepo support: Husky works in monorepos — install at root, hooks apply to all packages.
Custom hook directory: Husky v9 uses .husky/ by default. You can customize the path.
Troubleshooting: if hooks don't run, check that prepare script exists in package.json.
Non-JS projects: Husky works with any language — hooks are just shell scripts. Use Python, Go, Rust commands.
pre-commit framework. For language-agnostic projects, try lefthook. The concept is the same: store hooks in the repo, install them automatically.Lo kar liya — Key Points:
- ✅ Husky stores Git hooks in your project directory (.husky/) — tracked by Git, shared with team
- ✅ On npm install, Husky automatically installs hooks from .husky/ to .git/hooks/
- ✅ lint-staged runs linters ONLY on staged files — 10x faster than linting the entire project
- ✅ The combination: Husky triggers pre-commit → lint-staged runs → only staged files are checked
- ✅ lint-staged auto-fixes and re-stages formatted files — commit contains clean code automatically
- ✅ Every team member gets the same hooks after running npm install — no manual setup needed
- ✅ Set HUSKY=0 in CI to disable hooks — CI doesn't need local hooks
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login