husky v9: Git hooks through core.hooksPath, the prepare script and plain POSIX shell
Git hooks made easy 🐶 woof!
At a glance
- What is it?
- typicode/husky is a dependency-free npm package that points Git's core.hooksPath at a hooks directory in your repository so every clone runs the same client-side hooks. It is small and stable, but it only covers client-side hooks, demands a config migration from v4, and has not cut a release since v9.1.7 in November 2024.
- Who is it for?
- husky is a sound choice for a JavaScript or TypeScript repository that wants every contributor to run the same pre-commit or commit-msg checks without adding dependencies: it is a 2 kB package that hands the work to Git's own core.hooksPath. It is the wrong tool for enforcing rules that must hold for every push, because it only covers client-side hooks and those run on each developer's machine; keep the same checks in CI.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 7 months ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- typicode/husky35,392 ★ · +81
| Repository | 2026-09-13 | 2026-11-14 |
|---|---|---|
| typicode/husky | 35.3k | 35.4k |
81 Stars in the last 62 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
husky is a thin layer over Git's core.hooksPath
Git already knows how to run hooks; the problem husky solves is getting the same hooks onto every clone of a repository. Hooks placed in .git/hooks are not committed, so each developer would have to copy them in by hand. husky uses the newer Git setting core.hooksPath, which tells Git to look for hooks in a different directory. Point it at a directory that lives in the repository and is committed, and every clone that has the setting applied runs the same scripts.
That design explains the project's headline numbers. The package states it is about 2 kB gzipped with no dependencies, and that it runs in around 1 ms because there is little of it to run. It also explains the claim that husky adheres to Git's native hook organization: the hooks are ordinary hook files with the names Git expects, not entries in a JavaScript config object. The husky repository uses its own approach, with a .husky/ directory at the root next to bin.js, index.js and an extensionless husky script.
Getting husky: an npm package wired in through the prepare script
husky is published on npm under the package name husky, currently at version 9.1.7, and the installation and setup steps are on the documentation site at typicode.github.io/husky. The package exposes a husky command through its bin entry, which maps to bin.js, and a programmatic entry point through exports, which maps to index.js, with TypeScript declarations in index.d.ts.
The project aligns with npm best practice by using the prepare script. prepare is the npm lifecycle script that runs after a local install, so adding the husky setup there means a developer who clones the repository and installs dependencies gets hooks configured without a separate step. The consequence is that hook setup depends on that script actually running. Any environment that installs packages with lifecycle scripts turned off, or that never runs an install in the repository root, will not have core.hooksPath set, and hooks will silently not run there. The project lists opt-in and opt-out options for controlling this; check the documentation for the exact switches before relying on them in CI or containers.
Hooks are POSIX shell scripts, not JavaScript configuration
husky's answer to advanced cases is to use POSIX shell. A hook file is a script, so anything you can express in sh, such as conditional checks, calls to linters or test runners, or early exits, goes directly in the file. Branch-specific hooks fall out of the same idea: the project lists them as supported, and a shell script can inspect the current branch and decide what to run.
The repository treats its own shell code with care: there is a .shellcheckrc at the root, which configures ShellCheck, and a test.sh plus a test/ directory. The practical consequence for users is that your hooks are only as portable as your shell. husky lists macOS, Linux and Windows as supported, along with Git GUIs and Node version managers, but a hook that uses bash-only syntax or a tool installed on one machine and not another will fail for some contributors. Keep hook scripts to POSIX sh and call project-local tools through your package manager so the same command works for everyone.
Only the 13 client-side hooks, so enforcement stops at the developer's machine
husky supports all 13 client-side Git hooks, the ones Git runs in a local working copy such as pre-commit, commit-msg and pre-push. It does not and cannot install server-side hooks, because those run on the machine hosting the repository, not in a clone.
This is the most important limit to understand before treating husky as a policy tool. Every check it runs happens on the contributor's computer, under their control. A contributor whose clone never had core.hooksPath set, because the prepare script did not run, never runs the hooks. So husky is a convenience that catches mistakes early and keeps feedback fast, not a guarantee. Formatting, linting, commit message rules and tests that must hold for every change belong in CI as well, where they run on every push regardless of local setup. Treat husky hooks as a fast first pass that mirrors the CI checks, not as a replacement for them.
Moving from v4 to v9 means rewriting your configuration
The README flags upgrading from v4 to v9 as important: it requires migrating the previous configuration, and the documentation site covers how. The v9 changelog, published with the v9.0.1 release, lists what changed. The shift follows from the design above. Older major versions configured hooks differently, while v9 relies on core.hooksPath and hook files in a committed directory.
For a team on husky v4, the consequence is that bumping the version in package.json is not enough. Hooks defined in the old format will stop running after the upgrade until they are moved into the new layout, and nothing in Git will warn you that a hook you expected is missing. Do the upgrade on a branch, migrate each hook, and then deliberately trigger each one, for example by making a commit that should be rejected, to confirm it still fires. The same applies to monorepos and nested projects, which husky lists as supported, because the hooks directory location relative to the Git root is where migrations go wrong.
Node 18 and an ES module package
package.json declares engines node >=18 and sets type to module, so husky ships as an ES module. For most current projects that is not a constraint, but repositories pinned to an older Node release, or tooling that loads husky through CommonJS require from the programmatic API, should check compatibility before upgrading. The command line route through the husky bin is unaffected by the module format, since it runs as its own process.
The absence of dependencies matters here as well. husky adds nothing else to your node_modules tree and no transitive packages to audit, which is a real benefit for a tool that runs on every developer machine and has hooks into every commit. The trade-off is that features stay deliberately small: anything beyond setting up the hooks path and running scripts is left to the shell and to the tools your hooks call.
No release since v9.1.7 in November 2024
The latest tags are v9.1.5 on 2024-08-20, v9.1.6 on 2024-09-11 and v9.1.7 on 2024-11-18, and package.json in the repository still reads 9.1.7. The last push to the main branch was on 2026-03-19, more than seven months back. The project is not archived, but it should not be described as under active development.
For a tool this small, a quiet release line is less alarming than it would be for a framework: husky does one job on top of a stable Git feature and has no dependencies to fall out of date. The practical consequence is about fixes, not features. If you hit an edge case with a particular Git GUI, Node version manager or Windows shell, do not count on a quick upstream release; be prepared to work around it in your hook scripts. The repository accepts sponsorship through GitHub Sponsors and Open Collective, which the maintainer points to as the way to support continued work.
Editorial conclusion
husky is a sound choice for a JavaScript or TypeScript repository that wants every contributor to run the same pre-commit or commit-msg checks without adding dependencies: it is a 2 kB package that hands the work to Git's own core.hooksPath. It is the wrong tool for enforcing rules that must hold for every push, because it only covers client-side hooks and those run on each developer's machine; keep the same checks in CI. Before adopting it, confirm your toolchain runs on Node 18 or newer, check whether anything else in the repository already sets core.hooksPath, and if you are on husky v4, plan the configuration migration the project says is required.
Frequently asked questions
How does husky make Git hooks shareable across a team?
husky uses Git's core.hooksPath setting to point Git at a hooks directory committed in the repository, and it is set up through npm's prepare script so a normal install configures it on each clone.
Does husky v9 work with a husky v4 configuration?
No. The husky README states that upgrading from v4 to v9 requires migrating the previous configuration, and the migration steps are on the documentation site.
What Node version does husky need?
husky 9.1.7 declares node >=18 in its package.json engines field and ships as an ES module with no dependencies.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/typicode-husky)Star history chart for your README
[](https://hysenlabs.com/projects/typicode-husky)Was this useful?