Skip to content

Instantly share code, notes, and snippets.

@uncledevhq
Created June 24, 2025 10:48
Show Gist options
  • Save uncledevhq/25937a93e7d7964a9778dc490630f7a5 to your computer and use it in GitHub Desktop.
Save uncledevhq/25937a93e7d7964a9778dc490630f7a5 to your computer and use it in GitHub Desktop.
Husky & Pre-commit Setup Guide - Bongohive consult

Husky & Pre-commit Setup Guide

This guide explains how to set up Husky and pre-commit protection for your Next.js project to ensure code quality and consistency across your development team.

What is Husky?

Husky is a tool that allows you to easily add Git hooks to your project. Git hooks are scripts that run automatically before or after Git commands like commit, push, etc. This setup helps maintain code quality by running checks before code is committed.

What is lint-staged?

lint-staged runs linters on staged files only, which makes the process much faster than running linters on the entire codebase.

Prerequisites

  • Node.js (v16 or higher)
  • Yarn or npm
  • Git repository

Step 1: Install Dependencies

First, install the required development dependencies:

# Using yarn
yarn add -D husky lint-staged prettier eslint-config-prettier eslint-plugin-prettier

# Using npm
npm install --save-dev husky lint-staged prettier eslint-config-prettier eslint-plugin-prettier

Step 2: Configure ESLint

Create or update your .eslintrc.json file:

{
  "extends": ["next/core-web-vitals", "prettier"],
  "plugins": ["prettier"],
  "rules": {
    "prettier/prettier": "error",
    "no-console": [
      "warn",
      {
        "allow": ["warn", "error"]
      }
    ],
    "no-debugger": "error",
    "no-alert": "error",
    "prefer-const": "error",
    "no-var": "error",
    "object-shorthand": "error",
    "prefer-template": "error"
  },
  "env": {
    "browser": true,
    "es2020": true,
    "node": true
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  },
  "ignorePatterns": [
    "node_modules/",
    ".next/",
    "out/",
    "build/",
    "dist/",
    "*.config.js",
    "*.config.ts"
  ]
}

Step 3: Configure Prettier

Create a .prettierrc file:

{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 80,
  "tabWidth": 2,
  "useTabs": false,
  "bracketSpacing": true,
  "bracketSameLine": false,
  "arrowParens": "avoid",
  "endOfLine": "lf",
  "quoteProps": "as-needed",
  "jsxSingleQuote": true,
  "proseWrap": "preserve"
}

Create a .prettierignore file to exclude certain files:

# Dependencies
node_modules/
.pnp
.pnp.js

# Production builds
.next/
out/
build/
dist/

# Environment files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Coverage directory used by tools like istanbul
coverage/

# nyc test coverage
.nyc_output

# Dependency directories
jspm_packages/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
public

# Storybook build outputs
.out
.storybook-out

# Temporary folders
tmp/
temp/

# Editor directories and files
.vscode/
.idea/
*.swp
*.swo
*~

# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# Package files
package-lock.json
yarn.lock
pnpm-lock.yaml

# Config files that should not be formatted
*.config.js
*.config.ts

Step 4: Update package.json Scripts

Add the following scripts to your package.json:

{
  "scripts": {
    "build": "next build",
    "dev": "next dev",
    "lint": "next lint",
    "lint:fix": "next lint --fix",
    "format": "prettier --write .",
    "format:check": "prettier --check .",
    "type-check": "tsc --noEmit",
    "pre-commit": "lint-staged",
    "prepare": "husky",
    "start": "next start"
  }
}

Step 5: Configure lint-staged

Add the lint-staged configuration to your package.json:

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

Step 6: Initialize Husky

Run the following command to initialize Husky:

yarn prepare
# or
npm run prepare

Step 7: Create Pre-commit Hook

Create the pre-commit hook file:

npx husky add .husky/pre-commit "yarn pre-commit"

Then, replace the content of .husky/pre-commit with:

#!/usr/bin/env sh
[ -n "$CI" ] && exit 0

# Check if lint-staged is installed
if ! command -v lint-staged &> /dev/null; then
  echo "lint-staged could not be found, please install it with 'npm install lint-staged' or 'yarn add lint-staged'"
  exit 1
fi

echo "πŸ” Running pre-commit checks..."

# Run type checking
echo "πŸ“ Checking TypeScript types..."
yarn type-check || {
  echo "❌ TypeScript errors found. Please fix them before committing."
  exit 1
}

# Run linting
echo "πŸ”§ Running ESLint..."
yarn lint || {
  echo "❌ ESLint errors found. Please fix them before committing."
  exit 1
}

# Run build check
echo "πŸ—οΈ  Checking build..."
yarn build || {
  echo "❌ Build failed. Please fix build errors before committing."
  exit 1
}

# Run lint-staged (formatting and auto-fix)
echo "✨ Running lint-staged..."
yarn lint-staged || {
  echo "❌ Lint-staged failed. Please fix formatting issues before committing."
  exit 1
}

echo "βœ… All pre-commit checks passed!"

Make the hook executable:

chmod +x .husky/pre-commit

Step 8: Verify Setup

Test your setup by making a small change and trying to commit:

# Make a change to any file
echo "// test" >> src/test.js

# Try to commit
git add .
git commit -m "test commit"

If everything is set up correctly, you should see the pre-commit checks running before the commit is allowed.

What Each Check Does

1. TypeScript Type Checking (yarn type-check)

  • Runs tsc --noEmit to check for TypeScript errors
  • Ensures type safety across your codebase

2. ESLint (yarn lint)

  • Checks for code quality issues
  • Enforces coding standards
  • Catches potential bugs

3. Build Check (yarn build)

  • Ensures your code can be built successfully
  • Catches build-time errors early

4. lint-staged

  • Runs ESLint with auto-fix on staged files
  • Formats code with Prettier
  • Only processes files that are about to be committed

Customization Options

Adding More Checks

You can add additional checks to the pre-commit hook:

# Add unit tests
echo "πŸ§ͺ Running tests..."
yarn test || {
  echo "❌ Tests failed. Please fix them before committing."
  exit 1
}

# Add security audit
echo "πŸ”’ Running security audit..."
yarn audit || {
  echo "❌ Security vulnerabilities found. Please fix them before committing."
  exit 1
}

Modifying lint-staged Configuration

You can customize which files are processed and what actions are taken:

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

Skipping Pre-commit Hooks

In emergency situations, you can skip the pre-commit hooks:

git commit -m "emergency fix" --no-verify

Troubleshooting

Common Issues

  1. Hook not running: Make sure the hook file is executable (chmod +x .husky/pre-commit)

  2. Permission denied: Run chmod +x .husky/pre-commit

  3. lint-staged not found: Install it with yarn add -D lint-staged

  4. ESLint errors: Fix the errors manually or run yarn lint:fix

  5. Prettier conflicts: Run yarn format to format all files

Disabling Hooks Temporarily

# Skip all hooks for one commit
git commit -m "message" --no-verify

# Skip specific hooks
HUSKY=0 git commit -m "message"

Best Practices

  1. Keep hooks fast: Only run essential checks in pre-commit hooks
  2. Use lint-staged: Only process staged files for better performance
  3. Provide clear error messages: Help developers understand what needs to be fixed
  4. Document your setup: Keep this guide updated for your team
  5. Regular maintenance: Update dependencies and configurations regularly

CI/CD Integration

For continuous integration, you can run the same checks:

# Example GitHub Actions workflow
name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
      - run: yarn install
      - run: yarn type-check
      - run: yarn lint
      - run: yarn build
      - run: yarn test

This setup ensures that your code quality standards are maintained both locally and in your CI/CD pipeline.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment