Enforce Code Standards at Commit with husky + lint-staged

This article explains how to use husky and lint-staged to automatically run ESLint and Prettier on staged files before each Git commit, preventing non-compliant code from entering the repository, with step-by-step setup for husky v9, configuration details, common pitfalls, and the complete commit workflow.

Code Farmer Manor Chronicle
Code Farmer Manor Chronicle
Code Farmer Manor Chronicle
Enforce Code Standards at Commit with husky + lint-staged

Why Enforce Checks at Commit Time

Relying on teammates to manually run linting is unreliable; manual npm run lint is easily forgotten; running lint on the entire project becomes slow as the codebase grows. The solution combines husky to manage Git hooks and lint-staged to run checks only on staged files, making the process automatic, fast, and precise.

husky Version Differences

husky acts as a Git hooks manager, avoiding manual shell scripts in .git/hooks. A key version difference exists:

v4 and below: Configuration via package.json under husky.hooks.

v5+ (current mainstream, v9 used here): Command-line initialization creates a .husky/ directory where hook scripts live.

Pitfall: Old tutorials showing husky.hooks in package.json do not work with v9. Verify version with npm view husky version before installing.

Complete Installation Steps (husky v9)

# 1. Install husky and lint-staged
npm install -D husky lint-staged

# 2. Initialize (creates .husky/ folder at project root)
npx husky init

Initialization typically adds npm pkg set scripts.prepare="husky" to package.json. The core work is editing .husky/pre-commit to contain a single line:

# .husky/pre-commit content
npx lint-staged

This ensures that on every commit, only staged files are passed to lint-staged.

Configuring lint-staged: Check Only Staged Files

Add a lint-staged section to package.json mapping file patterns to commands:

{
  "lint-staged": {
    "*.{js,vue,jsx,ts}": "eslint --fix --cache",
    "*.{js,vue,jsx,ts,css,md}": "prettier --write"
  }
}

Line-by-line meaning:

JavaScript, Vue, JSX, TypeScript files → run eslint --fix --cache (auto-fix what it can, error on the rest).

Those plus CSS and Markdown → run prettier --write to enforce formatting. --cache speeds up subsequent runs by only checking changed files.

lint-staged's advantage: it only processes files that have been git add ed, so a small change stays fast and avoids re-linting untouched legacy code.

Full Commit Workflow

git add .
git commit -m "feat: add button component"

At commit time:

The pre-commit hook is triggered by husky.

lint-staged collects the staged files.

It runs ESLint and Prettier sequentially on those files.

If any unfixable errors appear, the commit is aborted ; the terminal shows file paths and line numbers. Fix, re-add, and commit again.

If all pass, the commit succeeds.

Typical experience: ESLint auto-fixes quotes/semicolons, files are modified, and the terminal prompts to re-add and commit.

Common Pitfalls and Fixes

Windows hook not executing / permission issues: Ensure .git/hooks belongs to the current project. New husky uses npx so manual permission changes are rarely needed; on Mac/Linux run chmod +x .husky/pre-commit if Permission denied occurs.

lint-staged config changes not taking effect: Verify npx lint-staged exists in pre-commit; commit once to test.

Temporarily skipping checks: Use git commit --no-verify -m "..." for emergencies, but restrain from routine use.

Leftover old husky configuration: When migrating to v9, remove the husky.hooks section from package.json and rely solely on the .husky/ directory.

Summary

husky: manages hooks → npx husky init generates .husky/
pre-commit contains npx lint-staged
lint-staged: checks only staged files, runs commands per extension
Result: code that fails ESLint / formatting cannot enter the repo

With this, the loop ESLint/Prettier config → editor format-on-save → pre-commit enforced checks is closed. To further standardize commit messages, add commitlint for a complete engineering standard.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Code Qualityfrontend engineeringESLintgit hooksPrettierhuskylint-stagedcommit linting
Code Farmer Manor Chronicle
Written by

Code Farmer Manor Chronicle

A heart like drifting clouds, ever at ease; a mind like flowing water, free to roam.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.