Back to blog

How to Set Up Husky and lint-staged: Enforcing Code Quality on Git Commit

In a team environment, maintaining code formatting and linting consistency is difficult. Even with configured ESLint and Prettier rules, developers will occasionally forget to format their code files or run checks before running a git push. This results in broken CI builds and cluttered code review diffs.

To prevent bad code from entering your repository, you should automate checks at the source using Git Hooks.

By combining Husky (a Git hook manager) and lint-staged (a utility to run tasks on staged files), you can force lint checks and auto-formatting to execute automatically every time someone runs git commit.

In this guide, we will examine Git Hooks, install Husky and lint-staged, and set up a pre-commit check pipeline.

Why lint-staged is Necessary

You can configure a Git pre-commit hook to run your project's complete lint script (e.g., npm run lint). However, this is highly inefficient.

As a project grows, scanning thousands of files on every minor code change takes minutes, frustrating developers.

lint-staged solves this:

  • It queries Git to find the specific files that are currently Staged (files added using git add that are about to be committed).
  • It executes lint and format tasks only on those staged files.
  • It auto-stages any quick-fixes (like automatic spacing adjustments) before final commit writes, keeping the commit process fast (usually under 2 seconds).

Step-by-Step Installation and Setup

Step 1: Install Dependencies

Run the installation command in your project root using pnpm (or npm/yarn):

pnpm add -D husky lint-staged

Step 2: Initialize Husky

Husky v9+ includes an initialization helper that configures Git hook directories and sets up package lifecycle scripts automatically:

npx husky init

This command performs two tasks:

  1. Creates a .husky/ configuration directory in your project root.
  2. Appends a "prepare": "husky" script to your package.json (this ensures that other developers installing dependencies will have Git hooks configured on their machines automatically).

Step 3: Configure lint-staged

Open your package.json file and declare your lint-staged configuration at the root object level:

"lint-staged": {
  "*.{js,jsx,ts,tsx}": [
    "eslint --fix",
    "prettier --write"
  ],
  "*.json": [
    "prettier --write"
  ]
}

This configuration rules:

  • If you commit .ts or .tsx files, ESLint attempts to auto-fix quality issues and Prettier formats the layout.
  • If you commit .json files, Prettier cleans up their margins and braces spacing.

Step 4: Link Husky to lint-staged

Open the generated .husky/pre-commit hook file in your editor. By default, it runs npm test. Overwrite it to run lint-staged:

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged

Testing the Configuration

Let's test if the commit intercept works:

  1. Create a dummy file test.ts with unformatted code and an unused variable:
const unusedVar = "bad format"   ;
  1. Stage the file for commit:
git add test.ts
  1. Run commit:
git commit -m "test pre-commit hook"

You will see Husky intercepting the commit in your terminal, executing ESLint and Prettier on test.ts. If ESLint catches unfixable errors (like a real syntax bug), the compile fails and the commit is blocked, preventing broken states from entering your git history.

Conclusion

Automating code quality checks in Git hooks protects repository health. By deploying Husky to manage hook scripts, using lint-staged to run formatting and linting tasks strictly on modified staged files, and blocking commits when quality thresholds fail, you establish an automated safeguard that intercepts formatting discrepancies and code bugs before they leave developer machines.