Skip to main content
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:
PreToolUse hooks run BEFORE Claude executes a tool.Use cases:
  • Validate operations before execution
  • Create backups before edits
  • Check permissions before dangerous operations
  • Block operations that violate policies
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "echo 'About to run bash command'"
      }]
    }]
  }
}

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

FieldRequiredDescription
matcherYesWhich tool triggers this hook (Edit, Bash, Write, etc.)
typeYesHook type: command or script
commandYesShell command or script to execute
descriptionNoHuman-readable description (removed during installation)

Tool Matchers

Hooks can match specific Claude Code tools:
MatcherTriggers 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

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"
      }]
    }]
  }
}

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

VariableDescription
$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

HooksCommands
Automatic executionManual invocation
Event-drivenUser-initiated
Background operationInteractive
Simple commands/scriptsComplex workflows
No user inputAccepts 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

Auto-format Python

{
  "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

Build docs developers (and LLMs) love