Hooks = Rules That Run Automatically
Have you ever wished Claude Code could do things like “auto-format code every time a file is saved” or “block dangerous commands entirely”?
That’s exactly what Hooks do. They let you set up automatic rules that trigger whenever Claude Code performs certain actions.
Think of them like automatic alarms. Just like an alarm goes off at 7 AM every morning, Hooks auto-format code when a file is saved, or block dangerous commands when detected.
| Everyday Automation | Claude Code Hooks |
|---|---|
| 7 AM → alarm rings | File saved → auto-formatting |
| Front door opens → camera records | Command runs → safety check |
| Fridge temp high → alert | Error detected → Telegram notification |
| Fire detected → sprinklers activate | rm -rf detected → execution blocked |
| End of workday → computer locks | Session ends → work log saved |
One-line summary
Hooks = “When this happens, automatically do that” — rules you set up in advance. Configure once and they run on their own forever.
7 Hook Events — When Do They Trigger?
For Hooks to run automatically, you need to specify “when.” Claude Code offers 7 trigger points (events). In practice, you’ll only use 3-4 of them regularly.
⚙
PreToolUse
Runs right before Claude uses a tool. Use it to inspect and block dangerous commands.
★ Most commonly used hook
✅
PostToolUse
Runs right after Claude uses a tool. Use it for auto-formatting after file saves.
★ Most commonly used hook
🔔
Notification
Runs when Claude is waiting for user input. Send completion notifications via Telegram or Slack.
🚀
SessionStart
Runs when a Claude Code session begins. Use for environment checks or initial setup.
🚫
SessionEnd
Runs when a Claude Code session ends. Use for saving work logs or cleanup tasks.
💬
PreSendMessage
Runs just before Claude sends a response. Use to inspect or modify response content.
📝
PostSendMessage
Runs right after Claude sends a response. Use for response logging or post-processing.
No need to memorize all 7
In practice, just PreToolUse (blocking dangers) and PostToolUse (auto-formatting) are enough to start. Add others one at a time as needed.
Hooks = Claude Code’s Automatic Alarms
Set them once, and Claude handles what you used to do manually every time
How to Configure — One Place: settings.json
Hooks are configured in settings.json. Don’t overthink it — just write your rules in JSON format.
Settings File Locations — 3 Options
| Scope | File Location | Description |
|---|---|---|
| This project only | .claude/settings.json | Saved inside the project folder. Shareable with team |
| All my projects | ~/.claude/settings.json | Saved in home folder. Applies everywhere |
| This project (me only) | .claude/settings.local.json | Personal settings not committed to git |
Start with project-level settings (.claude/settings.json). Move hooks to the home folder later if you want them everywhere.
Basic Structure
{
"hooks": {
"event-name": [
{
"matcher": "which tools to apply to",
"hooks": [
{
"type": "command",
"command": "command to run"
}
]
}
]
}
}The structure looks complex but follows a pattern. Three key elements:
One of 7 events: PreToolUse, PostToolUse, etc.
Target specific tools like Bash, Write, Edit, or apply to all
Shell command or script path to execute
Exit Code Meanings
The number (exit code) returned by a hook script determines Claude Code’s behavior.
| Exit Code | Meaning | Claude Code Behavior |
|---|---|---|
0 | Success (no issues) | Proceed as normal |
1 | Warning | Proceed but show a warning message to the user |
2 | Block | Completely block the tool from being used |
Easy to remember
0 = Green light (pass), 1 = Yellow light (caution, proceed), 2 = Red light (stop, blocked). Think of it like a traffic light.
Example 1: Auto-Format on File Save
One of the most popular hooks is auto-formatting. Every time Claude edits a file, Prettier or ESLint runs automatically to enforce code style.
Instead of manually running npx prettier --write each time, set it up once as a hook and it’s automatic forever.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "npx prettier --write $CLAUDE_FILE_PATH"
}
]
}
]
}
}Let’s break down what this means:
Configuration Explained
PostToolUse
Runs right after a tool is used
Write|Edit
Only triggers for file Write or Edit tools
command
$CLAUDE_FILE_PATH is automatically replaced with the modified file path. It formats only the changed file.
Environment Variables Available in Hooks
Hook scripts can use environment variables provided by Claude Code to know which file was modified or what command was attempted.
| Variable | Value | Usage |
|---|---|---|
$CLAUDE_FILE_PATH | Modified file path | Format only that file |
$CLAUDE_TOOL_INPUT | Input passed to the tool (JSON) | Inspect command content |
$CLAUDE_SESSION_ID | Current session ID | Log recording |
ESLint + Prettier Combo
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "npx eslint --fix $CLAUDE_FILE_PATH && npx prettier --write $CLAUDE_FILE_PATH"
}
]
}
]
}
}Prettier must be installed
This hook requires Prettier to be installed in your project. Run npm install -D prettier first. Same for ESLint: npm install -D eslint.
Example 2: Auto-Block Dangerous Commands
Claude Code is powerful, but it can sometimes try to run dangerous commands. For example, rm -rf / could wipe your entire system.
Let’s create a hook that automatically blocks such commands. This time we use PreToolUse — it checks before a tool is used and blocks if dangerous.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/safety-check.sh"
}
]
}
]
}
}Since the command can get long, we separated it into a script file. Create .claude/hooks/safety-check.sh with the checking logic.
#!/bin/bash
# Claude Code safety check hook
# Exit codes: 0=pass, 1=warn, 2=block
# Read the input command from environment variable
INPUT="$CLAUDE_TOOL_INPUT"
# Dangerous pattern list
DANGEROUS_PATTERNS=(
"rm -rf /"
"rm -rf ~"
"rm -rf *"
"mkfs"
"dd if="
"> /dev/sda"
"chmod -R 777 /"
":(){ :|:& };:"
)
# Check each pattern
for pattern in "${DANGEROUS_PATTERNS[@]}"; do
if echo "$INPUT" | grep -q "$pattern"; then
echo "BLOCKED: Dangerous command detected."
echo "Pattern: $pattern"
exit 2 # Block
fi
done
# Warning patterns (not blocked, but caution shown)
WARNING_PATTERNS=(
"sudo"
"npm publish"
"git push --force"
"git reset --hard"
)
for pattern in "${WARNING_PATTERNS[@]}"; do
if echo "$INPUT" | grep -q "$pattern"; then
echo "WARNING: This command requires caution."
echo "Pattern: $pattern"
exit 1 # Warn (proceed allowed)
fi
done
# No issues found, pass
exit 0Exit Code Behavior
🟢
exit 0
Pass
Safe command, proceed
🟠
exit 1
Warning
Show caution message, proceed
🔴
exit 2
Block
Prevent execution entirely
Simple Inline Block (No Script File)
If you don’t want a separate script file, you can use a simple inline command:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "echo $CLAUDE_TOOL_INPUT | grep -q 'rm -rf' && exit 2 || exit 0"
}
]
}
]
}
}Script needs execute permission
If you created a separate script file, it needs execute permission. Run chmod +x .claude/hooks/safety-check.sh once. On Windows, this isn’t needed.
Example 3: Send Completion Notifications
When you give Claude Code a long task, you’d normally have to watch the screen. With a Notification hook, you get alerted automatically when work is done.
Desktop Notification (Simplest)
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code task complete!\" with title \"Claude Code\"'"
}
]
}
]
}
}{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "powershell -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code task complete!')\""
}
]
}
]
}
}Telegram Notification (Get Alerts Anywhere)
Using the Telegram Bot API, you can receive alerts on your phone even when you’re away.
#!/bin/bash
# Send Claude Code notification via Telegram
BOT_TOKEN="your-bot-token"
CHAT_ID="your-chat-id"
MESSAGE="Claude Code task complete."
curl -s -X POST \
"https://api.telegram.org/bot$BOT_TOKEN/sendMessage" \
-d "chat_id=$CHAT_ID" \
-d "text=$MESSAGE" \
> /dev/null
exit 0{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/notify-telegram.sh"
}
]
}
]
}
}What does empty matcher mean?
"matcher": "" means “run in all cases.” Notification events aren’t tied to specific tools, so leaving the matcher empty is standard.
Try It Yourself
Reading alone won’t cut it. Follow these exercises, starting with the easiest.
Exercise 1: Set Up Auto-Formatting Hook (Easiest)
Try this in a project with Prettier installed. After setup, ask Claude to edit a file and see it auto-format.
mkdir -p .claude# Create .claude/settings.json with this content
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "npx prettier --write $CLAUDE_FILE_PATH"
}
]
}
]
}
}Add console.log("hello world") to index.js.After Claude edits the file, verify that Prettier ran automatically!
Exercise 2: Create a Danger-Blocking Hook
Set up a hook that detects and blocks rm -rf.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "echo $CLAUDE_TOOL_INPUT | grep -q 'rm -rf' && exit 2 || exit 0"
}
]
}
]
}
}Run rm -rf / for meIf you see a “Blocked” message, success!
Exercise 3: Ask Claude Code Directly
The easiest approach is to just ask Claude Code to create it.
Set up Claude Code hooks for me.
1. Auto-run prettier after file edits
2. Block rm -rf commands
3. Desktop notification on task completion
Configure in .claude/settings.json.Verification Checklist
If hooks aren’t working, check that the script file has execute permission. Run chmod +x .claude/hooks/script.sh. Still not working? Ask Claude “The hook isn’t working, help me debug” and it’ll find the cause.
Tips + Sources
Practical tips for effective Hook usage.
1. Start small
Don’t build complex hooks from the start. Set up auto-formatting first, get the feel, then add blocking hooks and notifications one at a time. Complex hooks are harder to debug.
2. You can combine multiple hooks
Register multiple hooks on the same event. For example, PostToolUse can chain formatting + linting + type checking in sequence. If an earlier hook fails, later ones won’t run.
3. Matcher supports regex
Use | to match multiple tools like Write|Edit. Use .* for all tools, or specify exact tool names.
4. Share with your team
.claude/settings.json can be committed to Git. When the entire team uses the same safety rules and auto-formatting, code quality is maintained automatically. Personal settings go in settings.local.json.
5. Ask Claude to create hooks for you
If writing JSON by hand is hard, just tell Claude Code “create this hook for me.” It generates the settings.json and any needed scripts.
Hooks = Claude Code’s Automatic Alarms
Auto-format on save, auto-block dangers, auto-notify on completion. Set it once and you never have to think about it.
Even one hook makes a difference
Just setting up the auto-formatting hook will give you that “wow, this is so convenient” moment. From there, add more one at a time. No need to make everything perfect from the start.
References
- Anthropic Official Hooks Docs — docs.anthropic.com/en/docs/claude-code/hooks
- Claude Code Settings Guide — docs.anthropic.com/en/docs/claude-code/settings
- Prettier Official Docs — prettier.io/docs