⏱ Reading time: ~13 min | 🗓 Updated: June 2026
If you’ve been using Claude Code for more than a few days, you’ve likely noticed a pattern: Claude finishes a task, you check the output, verify it manually, run a linter, run your test suite, and then move on. Now imagine all of that happening automatically — every single time, with zero exceptions. That’s exactly what Claude Code Hooks deliver.
Google Trends data shows that searches for “Claude Code hooks” have surged over 400% in Q2 2026, reflecting rapid adoption by developers who want to go beyond basic AI assistance and build fully automated, production-grade workflows. In this guide, we cover everything: what hooks are, all 18 hook events, the four hook types, real-world examples, and best practices.
What Are Claude Code Hooks?
Claude Code Hooks are shell scripts or commands that run automatically at specific points in Claude’s agent loop — before a tool executes, after a tool executes, at session start, at session end, and more. Unlike prompts or instructions (which Claude can decide to reinterpret), hooks are deterministic: they always run, and Claude cannot skip them.
This makes hooks ideal for enforcing hard requirements: running linters, blocking dangerous commands, sending Slack notifications, logging all file changes, auto-committing after every edit, and much more.
Hooks vs. Instructions: Key Difference
| Feature | Instructions (CLAUDE.md) | Hooks |
|---|---|---|
| Execution guarantee | Best-effort (Claude interprets) | Always runs, 100% |
| Skippable by Claude | Yes | No |
| Can block tool execution | No | Yes (PreToolUse) |
| Can modify output | No | Yes (PostToolUse) |
| Suitable for security rules | Partial | Yes |
| Runs external scripts | No | Yes |
The Four Hook Types
Claude Code supports four hook types, each firing at a different stage of the agent loop:
1. PreToolUse
Runs before Claude executes any tool call. This is your chance to inspect, validate, or block an action before it happens. If your hook exits with a non-zero status code, Claude will not proceed with the tool call.
# Example: Block any rm -rf commands
#!/bin/bash
TOOL_INPUT="$CLAUDE_TOOL_INPUT"
if echo "$TOOL_INPUT" | grep -q "rm -rf"; then
echo "BLOCKED: Refusing to run destructive rm -rf command" >&2
exit 1
fi
exit 0
2. PostToolUse
Runs after a tool executes. Use this for logging, notifications, auto-formatting, test runs, or any action that should follow every Claude tool call. PostToolUse hooks receive the tool name, input, and output as environment variables.
# Example: Auto-run ESLint after every file write
#!/bin/bash
if [ "$CLAUDE_TOOL_NAME" = "write_file" ]; then
FILE="$CLAUDE_TOOL_OUTPUT_PATH"
if [[ "$FILE" == *.js || "$FILE" == *.ts ]]; then
npx eslint "$FILE" --fix
echo "ESLint auto-fix applied to $FILE"
fi
fi
exit 0
3. Notification
Fires when Claude is waiting for user input. Perfect for desktop notifications, Slack pings, or text messages so you don’t have to sit and watch Claude work.
# macOS desktop notification when Claude needs input
#!/bin/bash
osascript -e 'display notification "Claude Code needs your input!" with title "Claude Code" sound name "Glass"'
exit 0
4. Stop
Runs when Claude’s session ends or stops. Use this for cleanup tasks, final test runs, commit-and-push automation, or generating session summaries.
# Auto-commit all changes when Claude finishes
#!/bin/bash
cd "$CLAUDE_PROJECT_ROOT"
if git diff --quiet && git diff --staged --quiet; then
echo "No changes to commit"
else
git add -A
git commit -m "chore: Claude Code auto-commit $(date +%Y-%m-%d)"
echo "Auto-committed all changes"
fi
exit 0
All 18 Hook Events in Claude Code
| Event | Hook Type | When It Fires |
|---|---|---|
| PreToolUse | PreToolUse | Before any tool executes |
| PostToolUse | PostToolUse | After any tool executes |
| PreBashExecution | PreToolUse | Before a Bash command runs |
| PostBashExecution | PostToolUse | After a Bash command runs |
| PreFileWrite | PreToolUse | Before writing a file |
| PostFileWrite | PostToolUse | After writing a file |
| PreFileRead | PreToolUse | Before reading a file |
| PostFileRead | PostToolUse | After reading a file |
| PreWebSearch | PreToolUse | Before a web search |
| PostWebSearch | PostToolUse | After a web search |
| PreMCPToolUse | PreToolUse | Before an MCP tool call |
| PostMCPToolUse | PostToolUse | After an MCP tool call |
| SessionStart | Notification | When a session begins |
| SessionEnd | Stop | When a session ends |
| AgentStart | Notification | When a subagent starts |
| AgentEnd | Stop | When a subagent finishes |
| Notification | Notification | When Claude needs input |
| UserPromptSubmit | PreToolUse | Before user prompt is processed |
Configuring Hooks: settings.json
Hooks are configured in your Claude Code settings.json file. There are three configuration locations:
- Global:
~/.claude/settings.json— applies to all projects - Project:
.claude/settings.jsonin your repo root — applies to this project, shared via git - Local:
.claude/settings.local.json— project-level but gitignored (personal overrides)
{
"hooks": {
"PostToolUse": [
{
"matcher": "write_file",
"hooks": [
{ "type": "command", "command": "bash ~/.claude/hooks/post-write.sh" }
]
}
],
"PreToolUse": [
{
"matcher": "bash",
"hooks": [
{ "type": "command", "command": "bash ~/.claude/hooks/security-check.sh" }
]
}
],
"Notification": [
{
"hooks": [
{ "type": "command", "command": "bash ~/.claude/hooks/notify.sh" }
]
}
],
"Stop": [
{
"hooks": [
{ "type": "command", "command": "bash ~/.claude/hooks/session-end.sh" }
]
}
]
}
}
10 Real-World Hook Recipes for Developers
Here are 10 practical hooks you can drop into your Claude Code setup today:
1. Auto-Run Tests After Every File Change
# PostFileWrite: run pytest on changed module
#!/bin/bash
FILE="$CLAUDE_TOOL_OUTPUT_PATH"
if [[ "$FILE" == *.py ]]; then
MODULE=$(dirname "$FILE")
python -m pytest "$MODULE" --tb=short -q 2>&1 | tail -20
fi
exit 0
2. Security Scanning Before Any Bash Execution
# PreBashExecution: block dangerous patterns
#!/bin/bash
INPUT="$CLAUDE_TOOL_INPUT"
DANGEROUS=("rm -rf /" "dd if=" "mkfs" "chmod -R 777 /")
for pattern in "${DANGEROUS[@]}"; do
if echo "$INPUT" | grep -qF "$pattern"; then
echo "SECURITY BLOCK: $pattern" >&2
exit 1
fi
done
exit 0
3. Slack Notification When Claude Needs Input
# Notification hook: ping Slack
#!/bin/bash
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-type: application/json" \
--data "{"text":"Claude Code needs your input on $(basename $PWD)"}" \
> /dev/null
exit 0
4. Auto-Format Python Files with Black
# PostFileWrite: format Python with black and isort
#!/bin/bash
FILE="$CLAUDE_TOOL_OUTPUT_PATH"
if [[ "$FILE" == *.py ]]; then
black "$FILE" --quiet
isort "$FILE" --quiet
echo "Formatted: $FILE"
fi
exit 0
5. Git Auto-Commit After Session End
# Stop hook: auto-commit all changes
#!/bin/bash
cd "$CLAUDE_PROJECT_ROOT" || exit 0
if [ -n "$(git status --porcelain)" ]; then
git add -A
git commit -m "feat: Claude Code session $(date +%Y-%m-%d)"
fi
exit 0
6. Audit Log All File Changes
# PostFileWrite: append to audit log
#!/bin/bash
LOGFILE="$HOME/.claude/audit.log"
echo "$(date +%Y-%m-%d\ %H:%M:%S) | WRITE | $CLAUDE_TOOL_OUTPUT_PATH | $CLAUDE_SESSION_ID" >> "$LOGFILE"
exit 0
7. TypeScript Type-Check After Every Write
# PostFileWrite: run tsc --noEmit
#!/bin/bash
FILE="$CLAUDE_TOOL_OUTPUT_PATH"
if [[ "$FILE" == *.ts || "$FILE" == *.tsx ]]; then
cd "$(git rev-parse --show-toplevel 2>/dev/null || echo ".")"
npx tsc --noEmit 2>&1 | grep "error TS" | head -5
fi
exit 0
8. Block Writes to Production Config Files
# PreFileWrite: protect production config files
#!/bin/bash
FILEPATH="$CLAUDE_TOOL_INPUT_PATH"
for pattern in ".env.production" "prod.config" "secrets.yml"; do
if [[ "$FILEPATH" == *"$pattern"* ]]; then
echo "BLOCKED: Cannot write to $FILEPATH" >&2
exit 1
fi
done
exit 0
9. Generate Session Summary on Stop
# Stop hook: write session summary markdown
#!/bin/bash
SUMMARY="$CLAUDE_PROJECT_ROOT/.claude/session-$(date +%Y%m%d-%H%M).md"
echo "# Claude Code Session Summary" > "$SUMMARY"
echo "Date: $(date)" >> "$SUMMARY"
git diff --name-only HEAD 2>/dev/null >> "$SUMMARY"
exit 0
10. Enforce Branch Naming Conventions
# PreBashExecution: enforce branch naming
#!/bin/bash
INPUT="$CLAUDE_TOOL_INPUT"
if echo "$INPUT" | grep -q "git checkout -b"; then
BRANCH=$(echo "$INPUT" | grep -oP "(?<=-b )\S+")
if ! echo "$BRANCH" | grep -qP "^(feat|fix|chore|docs|refactor)/"; then
echo "BLOCKED: Branch must follow feat|fix|chore|docs|refactor/name" >&2
exit 1
fi
fi
exit 0
Hook Environment Variables Reference
| Variable | Description |
|---|---|
| CLAUDE_TOOL_NAME | Name of the tool being called (e.g., bash, write_file) |
| CLAUDE_TOOL_INPUT | Full input string passed to the tool |
| CLAUDE_TOOL_OUTPUT | Tool output (PostToolUse hooks only) |
| CLAUDE_TOOL_INPUT_PATH | File path for file-related tools |
| CLAUDE_TOOL_OUTPUT_PATH | Output file path (write operations) |
| CLAUDE_SESSION_ID | Unique session identifier |
| CLAUDE_PROJECT_ROOT | Root directory of the current project |
| CLAUDE_HOOK_EVENT | The event type that triggered this hook |
Best Practices for Claude Code Hooks
- Always exit with the right code: Exit 0 = success/proceed. Non-zero in PreToolUse = block the action.
- Keep hooks fast: Hooks run synchronously. A slow hook slows down Claude. Scope hooks narrowly.
- Use matchers: The
matcherfield lets you target specific tools or file patterns. Don’t run expensive hooks on everything. - Version-control project hooks: Commit
.claude/settings.jsonfor team-wide hooks. Usesettings.local.jsonfor personal preferences (gitignored). - Test hooks independently: Export mock env vars and run hook scripts in your terminal before deploying them.
- Avoid infinite loops: If a PostFileWrite hook writes a file, guard against recursive triggering with sentinel checks.
Conclusion
Claude Code Hooks transform the AI coding experience from a conversational back-and-forth into a reliable, automated engineering platform. Whether you want zero-exception test runs, security guardrails, real-time notifications, or automatic git commits — hooks give you a programmable layer of control that operates entirely outside Claude’s decision-making.
Start with a simple notification hook, add a PostFileWrite formatter, and gradually build out a hooks library that matches your team’s workflow. The official hooks documentation is the best place to explore all available options.
📚 Resources & Further Reading
Official Documentation
- Claude Code Hooks Guide — Official Docs
- Claude Code Settings Reference
- Claude Code Best Practices
- Claude Code Agents & Parallel Tasks
Community Resources
- 10 Best Claude Code Hooks — AY Automate
- Claude Code Hooks Guide 2026 — DEV Community
- Claude Code Best Practices Wiki — GitHub
- Claude Code Guide 2026: 25 Features — MarkTechPost