Hooks are event-driven automation triggers that execute actions automatically when Claude Code uses tools. They enable you to automate repetitive tasks, enforce standards, and create seamless workflows without manual intervention.
What Are Hooks?
Hooks are JSON configuration files (.json) stored in .claude/hooks/ that define:
Trigger events - When the hook should run (before/after tool use)
Matchers - Which tools trigger the hook (Edit, Bash, Write, etc.)
Actions - What to execute (commands, scripts, notifications)
Conditions - Optional filters for selective triggering
Hooks run automatically in the background. Claude Code executes them without requiring user interaction.
Hook Types
Claude Code supports two main hook types:
Hook Structure
Hooks are configured in settings files (.claude/settings.json or .claude/settings.local.json):
{
"description" : "Hook description" ,
"hooks" : {
"PostToolUse" : [
{
"matcher" : "Edit" ,
"hooks" : [
{
"type" : "command" ,
"command" : "npm run build"
}
]
}
]
}
}
Hook Fields
Field Required Description matcherYes Which tool triggers this hook (Edit, Bash, Write, etc.) typeYes Hook type: command or script commandYes Shell command or script to execute descriptionNo Human-readable description (removed during installation)
Hooks can match specific Claude Code tools:
Matcher Triggers On EditFile edits WriteFile creation BashShell command execution ReadFile reads GlobFile pattern searches GrepContent searches
Use specific matchers to avoid unnecessary hook execution. Matching Edit won’t trigger on Bash commands.
Hook Categories
Git Automation
Code Quality
Build Automation
Security
Notifications
Automate git operations:
git-commit-formatter - Format commit messages automatically
prevent-force-push - Block dangerous git operations
auto-add-changes - Stage changes automatically
commit-msg-validator - Enforce commit message standards
npx claude-code-templates@latest --hook git/git-commit-formatter
Example: {
"hooks" : {
"PreToolUse" : [{
"matcher" : "Bash" ,
"hooks" : [{
"type" : "command" ,
"command" : "if [[ $BASH_COMMAND =~ 'git push --force' ]]; then echo 'Force push blocked'; exit 1; fi"
}]
}]
}
}
Maintain code standards:
format-on-save - Auto-format code after edits
lint-on-change - Run linters automatically
run-tests - Execute tests after code changes
type-check - Run TypeScript checks
npx claude-code-templates@latest --hook automation/format-on-save
Example: {
"hooks" : {
"PostToolUse" : [{
"matcher" : "Edit" ,
"hooks" : [{
"type" : "command" ,
"command" : "npx prettier --write $FILE_PATH"
}]
}]
}
}
Trigger builds automatically:
build-on-change - Auto-build when files change
watch-mode - Start development server
asset-optimization - Optimize images/assets
cache-invalidation - Clear caches after builds
npx claude-code-templates@latest --hook automation/build-on-change
Example: {
"hooks" : {
"PostToolUse" : [{
"matcher" : "Edit" ,
"hooks" : [{
"type" : "command" ,
"command" : "if [[ -f package.json ]]; then npm run build 2>/dev/null || true; fi"
}]
}]
}
}
Enforce security policies:
secret-scanner - Detect exposed secrets
dependency-audit - Check for vulnerabilities
permission-validator - Verify file permissions
sensitive-file-blocker - Prevent access to sensitive files
npx claude-code-templates@latest --hook security/secret-scanner
Example: {
"hooks" : {
"PostToolUse" : [{
"matcher" : "Write" ,
"hooks" : [{
"type" : "command" ,
"command" : "trufflehog git file://$FILE_PATH"
}]
}]
}
}
Get notified of important events:
slack-notifier - Send Slack messages
discord-webhook - Discord notifications
email-alerts - Email notifications
desktop-notifications - System notifications
npx claude-code-templates@latest --hook automation/slack-notifier
Example: {
"hooks" : {
"PostToolUse" : [{
"matcher" : "Bash" ,
"hooks" : [{
"type" : "command" ,
"command" : "curl -X POST $SLACK_WEBHOOK -d '{ \" text \" : \" Command executed \" }'"
}]
}]
}
}
Real-World Examples
Example 1: Build on Change Hook
File : Component downloaded to settings file
{
"description" : "Automatically trigger build processes when source files change" ,
"hooks" : {
"PostToolUse" : [
{
"matcher" : "Edit" ,
"hooks" : [
{
"type" : "command" ,
"command" : "if [[ -f package.json ]] && grep -q ' \" build \" ' package.json; then npm run build 2>/dev/null || yarn build 2>/dev/null || true; elif [[ -f Makefile ]]; then make 2>/dev/null || true; elif [[ -f Cargo.toml ]]; then cargo build 2>/dev/null || true; fi"
}
]
}
]
}
}
Behavior :
Triggers after every file edit
Detects build tool (npm, make, cargo, etc.)
Runs appropriate build command
Silent failures (|| true) prevent interruptions
Example 2: Git Commit Validator Hook
File : Component configuration
{
"hooks" : {
"PreToolUse" : [
{
"matcher" : "Bash" ,
"hooks" : [
{
"type" : "command" ,
"command" : "if [[ $BASH_COMMAND =~ 'git commit' ]] && [[ ! $BASH_COMMAND =~ '(feat|fix|docs|style|refactor|test|chore):' ]]; then echo 'Error: Commit message must follow conventional commits format'; exit 1; fi"
}
]
}
]
}
}
Behavior :
Runs before bash commands
Checks if command is git commit
Validates conventional commit format
Blocks commit if format is invalid
Example 3: Python Script Hook
Some hooks reference external Python scripts:
Hook Configuration :
{
"hooks" : {
"PostToolUse" : [{
"matcher" : "Edit" ,
"hooks" : [{
"type" : "command" ,
"command" : "python3 .claude/hooks/custom-formatter.py $FILE_PATH"
}]
}]
}
}
Supporting Script : .claude/hooks/custom-formatter.py
The CLI automatically downloads supporting scripts when installing hooks.
Installing Hooks
Single Hook
npx claude-code-templates@latest --hook git/git-commit-formatter
You’ll be prompted to choose installation location:
User settings (~/.claude/settings.json) - All projects
Project settings (.claude/settings.json) - Shared with team
Local settings (.claude/settings.local.json) - Personal only
Multiple Hooks
npx claude-code-templates@latest \
--hook git/git-commit-formatter \
--hook automation/build-on-change \
--hook security/secret-scanner
With Category Prefix
npx claude-code-templates@latest --hook automation/build-on-change
Hooks merge with existing settings. Multiple hooks can trigger on the same tool.
Hook Execution Context
Hooks have access to context variables:
Environment Variables
Variable Description $FILE_PATHPath to file being operated on $TOOL_NAMEName of tool that triggered hook $PROJECT_ROOTProject root directory $BASH_COMMANDCommand being executed (Bash matcher only)
Example usage:
echo "File modified: $FILE_PATH "
notify-send "Claude Code" "Tool $TOOL_NAME executed"
Hook Best Practices
1. Silent Failures
Prevent hooks from interrupting workflow:
npm run build 2> /dev/null || true
The || true ensures the hook never fails.
2. Conditional Execution
Only run when necessary:
if [[ -f package.json ]] && grep -q '"test"' package.json ; then
npm test
fi
3. Fast Operations
Hooks should be quick:
# ✅ Good: Fast check
eslint $FILE_PATH
# ❌ Bad: Slow operation
npm run test:all # Runs entire test suite
4. Idempotent Actions
Hooks should be safe to run multiple times:
# ✅ Good: Safe to repeat
npm run format $FILE_PATH
# ⚠️ Caution: May cause issues
git commit -m "Auto commit" # Creates duplicate commits
5. Error Handling
Handle errors gracefully:
if ! command -v prettier & > /dev/null; then
echo "Prettier not installed, skipping format"
exit 0
fi
prettier --write $FILE_PATH
Hook Debugging
Debug hooks by adding logging:
echo "Hook triggered: $TOOL_NAME on $FILE_PATH " >> /tmp/hook.log
Or use verbose output:
set -x # Enable verbose mode
npm run build
set +x # Disable verbose mode
Disabling Hooks
Temporarily disable hooks:
Per-Session
Set environment variable:
export CLAUDE_DISABLE_HOOKS = true
Permanent
Remove hook configuration from settings file or comment out:
{
"hooks" : {
// "PostToolUse": [ ... ] // Disabled
}
}
Hook vs Command
Hooks Commands Automatic execution Manual invocation Event-driven User-initiated Background operation Interactive Simple commands/scripts Complex workflows No user input Accepts arguments
Hooks automate repetitive tasks. Commands handle complex workflows that need user input.
Advanced Hook Patterns
Chained Hooks
Multiple hooks on same matcher:
{
"hooks" : {
"PostToolUse" : [{
"matcher" : "Edit" ,
"hooks" : [
{ "type" : "command" , "command" : "npm run format" },
{ "type" : "command" , "command" : "npm run lint" },
{ "type" : "command" , "command" : "git add $FILE_PATH" }
]
}]
}
}
Hooks run sequentially in order.
Conditional Hooks
Use shell conditionals:
{
"hooks" : {
"PostToolUse" : [{
"matcher" : "Edit" ,
"hooks" : [{
"type" : "command" ,
"command" : "if [[ $FILE_PATH =~ \\ .test \\ . ]]; then npm test; fi"
}]
}]
}
}
Only runs tests if file is a test file.
Hook with External Scripts
Call custom scripts:
{
"hooks" : {
"PostToolUse" : [{
"matcher" : "Write" ,
"hooks" : [{
"type" : "command" ,
"command" : "bash .claude/hooks/post-write.sh $FILE_PATH"
}]
}]
}
}
Keep complex logic in external scripts for maintainability.
Common Hook Recipes
{
"hooks" : {
"PostToolUse" : [{
"matcher" : "Edit" ,
"hooks" : [{
"type" : "command" ,
"command" : "if [[ $FILE_PATH =~ \\ .py$ ]]; then black $FILE_PATH 2>/dev/null || true; fi"
}]
}]
}
}
Run Tests on Change
{
"hooks" : {
"PostToolUse" : [{
"matcher" : "Edit" ,
"hooks" : [{
"type" : "command" ,
"command" : "if [[ $FILE_PATH =~ src/ ]]; then npm run test:related $FILE_PATH 2>/dev/null || true; fi"
}]
}]
}
}
Notify on Bash Execution
{
"hooks" : {
"PreToolUse" : [{
"matcher" : "Bash" ,
"hooks" : [{
"type" : "command" ,
"command" : "notify-send 'Claude Code' 'Executing: $BASH_COMMAND'"
}]
}]
}
}
Next Steps
Browse Hooks Explore 45+ available hooks
Create Custom Hooks Build your own automation
Settings Configure hook behavior
Commands Learn about slash commands