Claude Code Hooks: Automate Your Workflow Like a Pro (2026 Guide)
Master Claude Code hooks with PreToolUse, PostToolUse, and custom automation. Auto-format code, block dangerous edits, and enforce quality gates.
Claude Code is powerful. But out of the box, it's just following its best judgment. Sometimes you need guarantees. You need certain actions to always happen—or never happen—regardless of what the LLM decides.
That's what hooks are for.
//What Are Claude Code Hooks?
Hooks are user-defined shell commands that execute at specific points in Claude Code's lifecycle. They provide deterministic control over behavior.
Think of hooks as middleware for your AI coding agent:
- PreToolUse — Runs before a tool executes. Can block or modify the action.
- PostToolUse — Runs after a tool completes. Great for formatting or validation.
- UserPromptSubmit — Runs when you submit a prompt.
- Stop — Runs when Claude finishes responding.
Instead of hoping Claude remembers to run Prettier, you configure a hook that runs Prettier every time a file is edited. Deterministic. Reliable.
//Hook Events
Claude Code provides several hook events that fire at different points in the workflow.
PreToolUse Hooks
PreToolUse hooks run before tool calls and can block them. This is your gatekeeper.
Use cases:
- Block edits to sensitive files (
.env,package-lock.json,.git/) - Prevent shell commands that could be destructive
- Validate inputs before expensive operations
- Modify tool inputs on the fly (v2.0.10+)
When a PreToolUse hook returns exit code 2, the action is denied and your error message is sent back to Claude. It learns and adjusts.
PostToolUse Hooks
PostToolUse hooks fire immediately after a tool completes successfully. Your script receives information about what happened.
Use cases:
- Auto-format files after edits (Prettier, gofmt, rustfmt)
- Run linters after code changes
- Log all executed commands for compliance
- Trigger notifications on specific events
- Validate output meets your standards
PostToolUse is perfect for "always do this after that" workflows.
Other Hook Events
- UserPromptSubmit — When you hit Enter on a prompt. Good for preprocessing or logging.
- PermissionRequest — When Claude requests permission for a tool (v2.0.45+). Can auto-allow or auto-deny.
- Stop — When the Claude Code agent finishes responding. Useful for summaries or notifications.
- SubagentStop — When a subagent finishes (v1.0.41+).
- SessionEnd — When the Claude Code session terminates.
//Configuration
Hooks are configured in your .claude/settings.json file. Here's the structure:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "your-script-here"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "your-post-script-here"
}
]
}
]
}
}
You can define hooks at three levels:
- User level —
~/.claude/settings.json - Project level —
.claude/settings.jsonin your repo - Enterprise level — Managed configuration
Project-level hooks override user-level for that project.
//Matchers
Matchers determine which tools trigger your hooks. They only apply to PreToolUse, PostToolUse, and PermissionRequest.
Simple matching:
"matcher": "Write"
Matches only the Write tool.
Multiple tools:
"matcher": "Write|Edit"
Matches Write or Edit.
Wildcards:
"matcher": "*"
Matches everything.
Important: Matchers are case-sensitive. "bash" won't match the Bash tool.
//Practical Examples
Auto-format TypeScript files
Run Prettier on every TypeScript file after it's edited:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | { read file_path; if echo \"$file_path\" | grep -q '\\.ts$'; then npx prettier --write \"$file_path\" 2>/dev/null; fi; }"
}
]
}
]
}
}
Block dangerous file edits
Prevent Claude from touching sensitive files:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "python3 -c \"import json, sys; d=json.load(sys.stdin); p=d.get('tool_input',{}).get('file_path',''); sys.exit(2 if any(x in p for x in ['.env','package-lock.json','.git/']) else 0)\""
}
]
}
]
}
}
Run tests after code changes
Automatically run relevant tests when source files change:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | { read f; if echo \"$f\" | grep -q 'src/.*\\.ts$'; then npm test --findRelatedTests \"$f\" 2>/dev/null || true; fi; }"
}
]
}
]
}
}
Desktop notifications when waiting
Get notified when Claude needs your input:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}
//Exit Codes & Control Flow
Hook exit codes control what happens next:
| Exit Code | Behavior |
|---|---|
| 0 | Success, continue normally |
| 2 | Deny the action (PreToolUse/PermissionRequest only) |
| Other | Hook failure, logged but execution continues |
For PreToolUse hooks, returning exit code 2 blocks the action and sends your stderr output back to Claude as feedback. Claude sees why it was blocked and can adjust its approach.
You can also use JSON decision control for more nuanced responses:
{"decision": "deny", "message": "Cannot edit .env files"}
{"decision": "allow"}
{"decision": "ask"}
//Best Practices
Start simple. Begin with one or two hooks and add more as you identify needs. Don't over-engineer upfront.
Keep hooks fast. Hooks run synchronously. A slow hook slows down every tool call. If you need heavy processing, run it async or in the background.
Use project-level hooks. Define hooks per-project to match that project's tooling. Your Go project uses gofmt; your TypeScript project uses Prettier.
Test your hooks. A broken hook can block all operations. Test with edge cases before relying on it.
Log judiciously. PostToolUse hooks are great for compliance logging, but don't log sensitive data like API keys or credentials.
Give Claude feedback. When blocking with PreToolUse, provide a clear message about why and what to do instead. "Cannot edit .env files—use the secrets manager instead."
Hooks transform Claude Code from a smart assistant into a reliable team member that respects your project's rules. Set them up once, and they enforce your standards on every operation.
Now go automate something.
Ready to supercharge your Claude Code setup?
One command analyzes your codebase and generates CLAUDE.md, skills, and agents tailored to your exact stack.