---
title: "Pre-Commit Hooks: Husky vs Native Git Hooks for Clean Commits"
description: "Compare Husky and Native Git Hooks for clean commits. Use automation with TypeScript, ESLint, & lint-staged to stop bugs before they enter Git history."
slug: "pre-commit-hooks-husky-vs-native-git-hooks-for-clean-commits"
url: "https://furkanbaytekin.dev/blogs/pre-commit-hooks-husky-vs-native-git-hooks-for-clean-commits"
author: "Furkan Baytekin"
category: "Software"
tags: []
published: 2026-05-12T12:54:37Z
updated: 2026-05-12T12:54:37Z
readTime: 5
cover: "https://myblogcms.furkanbaytekin.dev/uploads/cover/c6696fc3-ebbd-416f-ac73-691920622e05"
---

When I built a company-wide React TypeScript template, my main goal was to prevent any linting or TypeScript errors from reaching the repository. Nothing faulty should ever be committed. The solution was a pre-commit hook that runs `npx tsc -b && npx lint-staged` automatically.

## The Problem: Why Pre-Commit Protection Was Necessary

In team-based React development, maintaining code quality is challenging. Developers work under pressure, might forget to run full checks, or stage files with small issues. Without automation, these problems enter git history, break CI pipelines, increase review time, and create technical debt. Our template uses strict TypeScript and ESLint + Prettier. I needed a reliable guard that enforces `tsc -b` (fast build check) and lint-staged (only on staged files) before every commit succeeds.

## First Solution: Implementing with Husky

I started with Husky – the most popular tool for Git hooks in JavaScript projects. It automatically manages hooks during npm install.

**Installation steps:**

```bash
npm install --save-dev husky lint-staged
```

**package.json configuration:**

```json
{
  "lint-staged": {
    "*.{ts,tsx}": ["eslint --fix", "prettier --write"]
  }
}
```

**Husky setup:**

```bash
npx husky install
npx husky add .husky/pre-commit "npx tsc -b && npx lint-staged"
```

The actual hook file created was:

```sh
#!/usr/bin/env sh
npx tsc -b && npx lint-staged
```

This worked perfectly. Every `git commit` triggered checks. If successful, commit proceeded. If not, it aborted immediately with clear error messages. Team onboarding was simple – just `npm install` and hooks were ready.

Husky made the process smooth and developer-friendly for the first version of our template.

## The Conversation with My Friend

After using Husky for a while, I showed it to a senior developer friend.

**Me:** “Husky ensures no bad commits. Super easy.”

**Friend:** “It works, but why add an extra dependency? Git has native hook support built-in. For a company template, native is cleaner – zero overhead, no npm packages needed.”

**Me:** “But sharing hooks with the team?”

**Friend:** “Version control the script in `scripts/hooks/` and copy it to `.git/hooks/` via setup instructions. Simpler and lighter.”

His advice made perfect sense. Husky is convenient but adds unnecessary weight for a standard lint + type check setup. We decided to migrate to native Git hooks.

## Migrating to Native Git Hooks

We created a dedicated folder `scripts/hooks/` in the template repo.

**Final pre-commit script (`scripts/hooks/pre-commit`):**

```sh
#!/usr/bin/env sh
# Company React Template - Pre-Commit Hook
# Ensures no TS errors and clean linting before commit

echo "🔍 Running TypeScript check with tsc -b..."
npx tsc -b

if [ $? -ne 0 ]; then
  echo "❌ TypeScript compilation errors detected!"
  echo "Fix the issues and try committing again."
  exit 1
fi

echo "🔍 Running lint-staged..."
npx lint-staged

if [ $? -ne 0 ]; then
  echo "❌ Linting or formatting issues found!"
  echo "Run eslint or prettier locally if needed."
  exit 1
fi

echo "✅ All checks passed. Proceeding with commit."
exit 0
```

**Team setup instructions (added to README):**

```bash
# One-time setup after cloning
cp scripts/hooks/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
```

**Bonus: Auto-Setup via `package.json` `prepare` Script**

The one downside of native git hooks was the manual `cp` and `chmod` step after every clone. You can fully automate this using the `prepare` script in `package.json`:

```json
{
  "scripts": {
    "prepare": "cp scripts/hooks/pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit"
  }
}
```

The `prepare` script is a built-in npm lifecycle hook that runs automatically after `npm install`. So when a new team member clones the repo and runs `npm install`, the pre-commit hook is set up instantly — no extra steps, no extra packages.

This approach gives you the best of both worlds: Husky's zero-config onboarding experience with the lightness of native Git hooks.

This native version delivers exactly the same behavior as Husky but without any extra dependencies.

## What Happens When Checks Fail?

The hook provides instant feedback:

- Developer runs `git commit -m "add feature"`
- Script executes TypeScript check first
- Any type error (wrong interface, missing property, etc.) prints detailed output
- Commit aborts immediately – no commit is created
- Same for linting: ESLint errors or Prettier issues stop the process
- Developer sees exact file and line numbers, fixes them, stages again, and retries

This loop forces quality at the source. No more “I’ll fix it later” commits polluting the repo.

## Benefits of Native Git Hooks Over Husky

- No additional npm packages
- Smaller package.json and faster installs
- Works completely offline
- Lower performance overhead
- Full control and easy customization
- Better suited for company templates and shared boilerplates

Husky remains excellent for personal projects or when you need many complex hooks. For our use case – simple TS + lint enforcement – native Git hooks are superior.

## Results After Implementation

Since rolling out this hook in our React template:

- Lint-related PR comments dropped by about 80%
- CI pipeline failures from code style or type issues became rare
- New developers commit with confidence from day one
- Git history stays clean and professional
- Overall team velocity improved

The single file `scripts/hooks/pre-commit` turned into one of the most valuable parts of the boilerplate.

## Conclusion and Recommendations

Start with Husky if you want quick setup. Then migrate to native Git hooks for a minimal, professional template. Either way, pre-commit checks are essential for modern React TypeScript projects.

Implement this today to keep your repository clean, your CI green, and your team productive.
