Automating code quality checks at commit time — running PHPCS, ESLint, and Prettier only on the files being staged — prevents style violations and syntax errors from ever reaching the repository. Husky manages the Git hooks and lint-staged runs the linters against staged files only, keeping the pre-commit hook fast even in large WordPress projects.
Problem: A WordPress plugin's JavaScript and PHP code is committed without linting or formatting checks — the codebase accumulates inconsistent indentation, trailing whitespace, and ESLint errors that slip through code review.
Solution: Add Husky to run Git hooks automatically: npx husky init creates a .husky/ directory, and a pre-commit hook runs lint-staged. Configure lint-staged to run eslint --fix on staged JS files and phpcbf on staged PHP files — only changed files are linted, keeping the hook fast. Commit the Husky config to enforce the hooks across the team.
The setup below installs Husky and lint-staged, configures PHPCS and ESLint as pre-commit checks, adds a commit-msg hook to enforce Conventional Commits format, and shows how to skip hooks in emergency situations.
# 1. Install Husky and lint-staged
npm install --save-dev husky lint-staged
# 2. Initialise Husky (creates .husky/ directory and sets core.hooksPath)
npx husky init
# 3. Add pre-commit hook
echo 'npx lint-staged' > .husky/pre-commit
# 4. Add commit-msg hook (Conventional Commits format check)
cat > .husky/commit-msg << 'EOF'
#!/bin/sh
npx --no-install commitlint --edit "$1"
EOF
chmod +x .husky/commit-msg
# 5. Install commitlint
npm install --save-dev @commitlint/cli @commitlint/config-conventional
{
"lint-staged": {
"*.php": [
"composer run phpcs",
"composer run phpstan --no-interaction -- --no-progress"
],
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{css,scss}": [
"stylelint --fix",
"prettier --write"
],
"*.{json,md}": [
"prettier --write"
]
},
"commitlint": {
"extends": ["@commitlint/config-conventional"]
}
}
{
"scripts": {
"prepare": "husky",
"phpcs": "vendor/bin/phpcs --standard=WordPress --extensions=php",
"phpstan": "vendor/bin/phpstan analyse --level=6 src/"
}
}
# Skip hooks in an emergency (use sparingly)
git commit --no-verify -m "hotfix: revert breaking change"
# Run lint-staged manually without committing
npx lint-staged --verbose
NOTE: lint-staged only runs linters on staged files — files modified but not staged are untouched — so always use git add -p to review your staging area before committing; linting only staged content is intentional and prevents the hook from failing due to unrelated work-in-progress files.