Hooks
Run your own scripts automatically at key points in a session.
Hooks let you attach your own shell commands to specific moments in Claude Code's workflow — so things like formatting, linting, or blocking risky operations happen automatically, without you having to remember to run them.
What hooks are for
Think of hooks like git hooks (pre-commit, post-commit) but for Claude's tool calls instead of git operations. Every time Claude uses a tool — reading a file, writing code, running a shell command — hooks can fire before or after that action. You can use them to auto-format edited files, run a linter after every write, log activity, or outright block commands that match a dangerous pattern.
- Auto-run Prettier or ESLint after Claude edits a file
- Block shell commands that match a risky pattern (e.g. rm -rf)
- Send a desktop notification when Claude finishes a long task
- Inject extra context into every prompt before Claude sees it
Hook events
Each hook is tied to a named event in Claude Code's lifecycle. The most useful ones for everyday automation are:
- PreToolUse — fires before any tool call; returning exit code 2 blocks the call entirely
- PostToolUse — fires after a tool call succeeds; ideal for formatting or linting
- UserPromptSubmit — fires when you submit a prompt, before Claude processes it
- Stop — fires when Claude finishes responding (useful for notifications)
- SessionStart — fires once when a session begins
Configure a hook
Hooks live in a settings.json file. The structure has three levels: the event name, a matcher that filters which tool triggers the hook, and the hook handler itself. You can scope hooks to all your projects (~/.claude/settings.json) or to a single project (.claude/settings.json).
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$CLAUDE_FILE_PATHS\""
}
]
}
]
}
}The matcher field filters which tool calls trigger the hook. Use an exact tool name (Edit), a pipe-separated list (Edit|Write), or a regex for more complex patterns. Omit the matcher entirely to match every tool call.
Example: auto-format on save
The most common hook runs a formatter every time Claude writes or edits a file. Add this to your project's .claude/settings.json and Prettier will run automatically on every file Claude touches — no manual step needed.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$CLAUDE_FILE_PATHS\"",
"timeout": 10
}
]
}
]
}
}For blocking hooks (PreToolUse), exit with code 2 to prevent the tool call — Claude will see your stderr message and stop. Exit code 0 means success. Exit code 1 is a non-blocking error and does not stop anything.
Use hooks safely
- Exit code 2 blocks an action; exit code 1 does NOT — it is treated as a non-blocking error
- stdout must contain only valid JSON if you return a decision object — stray output will break parsing
- Quote file-path variables in your commands so paths with spaces do not break
- For hard security enforcement, prefer Claude Code's permission system over a hook