Featured image of post Automating Pre-Commit Checks with Husky and Git Hooks Featured image of post Automating Pre-Commit Checks with Husky and Git Hooks

Automating Pre-Commit Checks with Husky and Git Hooks

Configure ESLint, Prettier, and Jest tests to run automatically before every commit using Husky and lint-staged to protect code quality.

Introduction

In team development, simple mistakes like committing code with syntax errors or bad formatting are common. These issues can break the CI build or lead to messy formatting wars during code reviews.

Instead of relying on developer vigilance to enforce guidelines, you can automate these checks.

By combining Git Hooks with Husky, you can automatically trigger linters (ESLint), formatters (Prettier), and unit tests (Jest) before any commit or push is finalized. This guide walks you through setting up this workflow.


1. What are Git Hooks and Husky?

Git Hooks are scripts that run automatically whenever a key event (such as commit, push, or merge) occurs in a Git repository.

By default, these scripts live inside the .git/hooks/ directory. However, since the .git/ folder is excluded from version control (.gitignore), sharing these hooks across a team has historically been difficult.

Husky solves this problem by exposing Git Hooks to a project-level folder (e.g., .husky/). This allows team members to check hooks into Git and synchronize configurations automatically via npm packages.


2. Basic Husky Setup Steps

Run the following commands in your project repository:

Step 1: Install and Initialize

Use the husky-init CLI tool to install Husky and configure the initial directories:

# Initialize Husky configurations
npx husky-init && npm install

This command makes three modifications:

  1. Adds husky to your devDependencies in package.json.
  2. Adds a "prepare": "husky" script to package.json (which automatically installs hooks on other machines during npm install).
  3. Generates a .husky/ directory at the project root along with a default pre-commit hook file.

Step 2: Configure the Pre-Commit Hook

Open the generated .husky/pre-commit script. By default, it runs npm test. Update this command to run your linter:

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

# Run linter and abort commit if it returns an error code
npm run lint

Now, whenever you run git commit, Husky runs npm run lint. If ESLint detects errors, the commit process is aborted, keeping broken code out of your repository.


3. Optimizing Speed with lint-staged

By default, running linters on the entire project during every commit can make the process slow as the codebase grows.

To optimize performance, use lint-staged. This tool isolates checks so they run only on files currently staged in Git (the files you modified).

Installation and Setup

  1. Install the Package:

    npm install --save-dev lint-staged
    
  2. Add Configuration to package.json: Define which files to target and which commands to run:

    {
      "lint-staged": {
        "*.{js,ts,jsx,tsx}": [
          "eslint --fix",
          "prettier --write"
        ],
        "*.css": [
          "stylelint --fix",
          "prettier --write"
        ]
      }
    }
    
  3. Update the Pre-Commit Hook: Modify .husky/pre-commit to trigger lint-staged:

    #!/usr/bin/env sh
    . "$(dirname -- "$0")/_/husky.sh"
    
    npx lint-staged
    

With this configuration, only your modified files are formatted and linted. This keeps the pre-commit check fast while ensuring committed code remains clean.


Conclusion

Combining Husky with lint-staged automates code quality checks, saving time during code reviews and preventing broken builds.

  1. Manage Git Hooks at the project level using Husky.
  2. Auto-configure hook installations via the package lifecycle script (prepare).
  3. Keep checks fast by target-scanning only staged files with lint-staged.

Introduce Husky early in your project to keep your codebase consistent and clean.