What are Hooks?
are event-driven scripts that fire on specific lifecycle events during a Claude Code session. They run shell commands (bash, Python, Node.js) in response to events like tool calls, session stops, or user prompts.
Think of hooks as middleware — they intercept events and can warn, block, or augment behaviour automatically.
Hook Events
sequenceDiagram participant You participant Claude participant Tool participant Hook You->>Hook: UserPromptSubmit You->>Claude: Send message Claude->>Hook: PreToolUse Hook-->>Claude: allow / block Claude->>Tool: Execute tool Tool-->>Claude: Result Claude->>Hook: PostToolUse Claude->>You: Response Claude->>Hook: Stop
| Event | When It Fires | Typical Use |
|---|---|---|
| Before a tool is called | Block risky operations, warn about sensitive files | |
| After a tool completes | Run linter/formatter after file edits, run tests | |
Stop | When Claude finishes a response | Play a notification sound, send an alert |
UserPromptSubmit | When you send a message | Inject extra context, validate input |
What are hooks in Claude Code?
Configuration
Hooks are configured in your Claude Code settings (.claude/settings.json or global settings):
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "afplay /System/Library/Sounds/Glass.aiff"
}
]
}
]
}
}This plays a sound effect every time Claude finishes a response — useful so you don't have to watch the terminal.
The Verification Loop
The most impactful hook pattern is using PostToolUse to create a verification loop: after every file edit, automatically run tests or linting. Claude sees the results in context and self-corrects immediately.
{
"PostToolUse": [{
"matcher": "Edit",
"hooks": [{
"type": "command",
"command": "npx eslint --fix $FILE_PATH"
}]
}]
}Boris Cherny uses a similar pattern: a PostToolUse hook that runs bun run format || true after every edit. The || true ensures the hook doesn't block Claude if formatting fails — it just applies what it can.
This creates a tight feedback loop: Claude edits → formatter runs → Claude sees the result → adjusts its next edit accordingly. Quality improves automatically without you intervening.
Which hook event lets you block an action before it happens?
More Practical Examples
Notification when Claude needs you
{
"PermissionRequest": [{
"hooks": [{
"type": "command",
"command": "say 'Hey! I need your permission'"
}]
}]
}Run tests after edits
{
"PostToolUse": [{
"matcher": "Edit",
"hooks": [{
"type": "command",
"command": "npm test --silent 2>/dev/null || echo 'WARNING: Tests failed after edit'"
}]
}]
}Claude gets immediate feedback — if an edit breaks tests, the warning appears in context and Claude can self-correct.
Security Hooks
One of the most powerful applications is blocking edits to sensitive files. The PreToolUse event can reject tool calls before they execute:
{
"PreToolUse": [{
"matcher": "Edit",
"hooks": [{
"type": "command",
"command": "echo $FILE_PATH | grep -q 'src/core/auth\\|src/core/billing\\|.env' && echo 'BLOCKED: This file requires manual review' && exit 2 || exit 0"
}]
}]
}Exit code 2 tells Claude Code to reject the tool call. Claude can still read these files — it just can't modify them. Your team edits auth, billing, and environment files manually.
Hooks run as shell commands. They have full access to your local environment. A misconfigured hook could delete files or leak data. Test hooks by running the shell command manually first, and prefer project-scoped hooks over global ones for sensitive operations.
How do hooks differ from commands and skills?
Hooks vs Other Building Blocks
| Feature | Hooks | Commands | Skills |
|---|---|---|---|
| Triggered by | System events (automatic) | You typing /name | Context matching (automatic) |
| Can block actions | Yes (PreToolUse) | No | No |
| Runs code | Yes (shell scripts) | No (instructions for Claude) | No (instructions for Claude) |
| Cost | Free (local shell scripts) | API tokens (Claude processes) | API tokens (Claude processes) |
The key distinction: hooks run shell scripts, while commands and skills provide instructions for Claude. Hooks are the only building block that executes code outside of Claude's model.
When should you use a hook instead of a CLAUDE.md instruction?
- Using hooks for things needing Claude's judgment. Hooks are rigid — same script every time. If you need nuance ("run the linter, but skip auto-generated files"), use a CLAUDE.md instruction or a command instead.
- Writing slow hooks. Every
PostToolUsehook runs after every tool call. A 5-second hook adds 5 seconds after every file edit, bash command, and search. Keep hooks under 1 second. - Not testing hooks in isolation. Run the shell command manually first. A broken hook can silently pass (exit code 0 = Claude proceeds) or block everything (exit code 2 on every tool call = Claude can't do anything).
Key Takeaway
Hooks are the automation layer — they react to events without you typing anything. The verification loop (PostToolUse hooks that test or lint after edits) is the single most valuable hook pattern, creating automatic quality feedback that compounds with every session.
