Table of Contents
- What are Pre-Commit Hooks?
- Why Use Husky and lint-staged?
- Installing Husky and lint-staged
- Basic Setup for Husky
- Configuring lint-staged
- Full Example: TypeScript + ESLint + Prettier Workflow
- Best Practices
- Conclusion
What are Pre-Commit Hooks?
Pre-commit hooks are scripts that automatically run before you commit your code to Git.
You can use them to:
- Lint code
- Format code
- Run tests
- Prevent bad commits
This saves you (and your team) from introducing broken or badly formatted code into the repository.
Why Use Husky and lint-staged?
- Husky: Makes it super easy to manage Git hooks like pre-commit, pre-push, etc.
- lint-staged: Runs linters and formatters only on changed (staged) files, making it fast.
Together, they ensure only clean, well-formatted code gets committed — without slowing down developers.
Installing Husky and lint-staged
At the root of your monorepo:
# Install both as dev dependencies
npm install --save-dev husky lint-staged
or if you’re using yarn:
yarn add -D husky lint-staged
Basic Setup for Husky
- Enable Husky in your repo:
npx husky install
- Make sure it’s installed automatically after npm install:
In your package.json
:
{
"scripts": {
"prepare": "husky install"
}
}
Theprepare
script makes sure Husky hooks are available after someone installs dependencies.
- Add a Pre-commit Hook:
npx husky add .husky/pre-commit "npx lint-staged"
This creates a .husky/pre-commit
file that will run lint-staged
every time you commit.
Configuring lint-staged
Now configure lint-staged in your package.json
:
{
"lint-staged": {
"**/*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"**/*.{js,json,css,md,yml}": [
"prettier --write"
]
}
}
Explanation:
- For
.ts
and.tsx
files:- First, run ESLint and auto-fix issues.
- Then, format with Prettier.
- For other file types (
.js
,.json
,.css
,.md
,.yml
):- Only format using Prettier.
Full Example: TypeScript + ESLint + Prettier Workflow
Your package.json
would look like:
{
"scripts": {
"prepare": "husky install",
"lint": "eslint . --ext .ts,.tsx",
"format": "prettier --write ."
},
"devDependencies": {
"husky": "^9.0.0",
"lint-staged": "^15.0.0",
"eslint": "^8.0.0",
"prettier": "^3.0.0",
"typescript": "^5.0.0"
},
"lint-staged": {
"**/*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"**/*.{js,json,css,md,yml}": [
"prettier --write"
]
}
}
And your .husky/pre-commit
file would contain:
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
Best Practices
✅ Keep pre-commit hooks lightweight.
(Only lint and format staged files, not full project.)
✅ Fail fast.
If lint or formatting fails, block the commit.
Fix and re-stage before committing again.
✅ Integrate with CI/CD.
Pre-commit is local safety.
You should still run full lints/tests in CI pipelines to catch missed issues.
✅ Keep Husky configuration in Git
(.husky
directory should be committed.)
Conclusion
Setting up Husky + lint-staged creates an automatic quality gate for your TypeScript monorepo.
Without even thinking about it, your team will:
- Catch ESLint errors early
- Maintain formatting standards
- Save time during code review
- Ship cleaner code
Summary Workflow:
Developer → git add → git commit → (Husky triggers) → lint-staged runs → commit succeeds or fails based on code quality ✅