"Format automatically after every change." "Stop dangerous commands before they run." Hooks are how you bake these rules into Claude Code: run arbitrary actions at specific lifecycle events. This article walks through where to configure them, the key events, and how blocking works, from a practical angle, based on the official docs.
This article is compiled by Hashito System from the official Claude Code documentation (Anthropic). Hook behavior and event names may be added or changed between versions. Please confirm the latest and exact details in the primary source linked at the end.
1. What a hook is
A hook is a user-defined action that runs automatically at a specific event in the Claude Code lifecycle. It can intervene before or after a tool runs, or when a response is about to stop, to automate shell commands, formatting, and policy enforcement. Unlike asking in a prompt, a hook runs deterministically, every time.
2. Where to configure them
Hooks are configured in JSON. The main locations, in order of precedence, are:
~/.claude/settings.json— user-global (shared across all projects)..claude/settings.json— project-specific. Shareable via version control..claude/settings.local.json— project-specific and gitignored (personal).
3. Key hook events
There are many events; the most useful ones to know first are:
| Event | When it fires |
|---|---|
PreToolUse | Just before a tool runs. You can block or rewrite the input here. |
PostToolUse | Right after a tool runs. Good for formatting or logging. |
UserPromptSubmit | When the user submits a prompt. |
Stop | When Claude is about to finish responding. |
SubagentStop | When a subagent stops. |
SessionStart / SessionEnd | At the start and end of a session. |
Notification | When Claude Code sends a notification. |
4. Writing the config (matcher and command)
The config nests three levels: event name, then matcher, then a hooks array. The matcher narrows which tools apply; use a pipe such as Edit|Write for several, or omit it (or use *) to match all tools. Here is an example that auto-formats after a file is written:
# .claude/settings.json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$CLAUDE_PROJECT_DIR\""
}
]
}
]
}
}
Hooks receive the event information as JSON on standard input (stdin). Common fields include session_id, cwd, and hook_event_name; tool events also pass tool_name and tool_input. Paths can use placeholders such as ${CLAUDE_PROJECT_DIR} (the project root).
5. Controlling behavior with exit codes
A hook's exit code determines what happens next. This is easy to get wrong, so it matters.
| Exit code | Behavior |
|---|---|
| 0 | Success. Stdout is parsed as structured JSON output. Does not block. |
| 2 | Blocking error. Stdout JSON is ignored; stderr is fed to Claude as an error message. For PreToolUse and similar, the action is blocked. |
| Other | Non-blocking error for most events. Execution continues; stderr appears in the transcript. |
exit 1 is treated as non-blocking and will not stop anything. Getting this wrong leads to "I thought I blocked it, but I didn't" incidents.
Here is a minimal PreToolUse example that stops a dangerous delete command. It writes the reason to stderr and exits with code 2.
# .claude/hooks/block-rm.sh (called from PreToolUse) input=$(cat) # read JSON from stdin cmd=$(printf '%s' "$input" | grep -o '"command"[^,}]*') case "$cmd" in *"rm -rf"*) echo "rm -rf is not allowed." >&2 exit 2 ;; # block with exit code 2 esac exit 0
6. When to reach for a hook
- Auto-formatting — run Prettier / gofmt in
PostToolUseto keep every diff clean. - Guardrails — block dangerous commands or writes to sensitive paths in
PreToolUse. - Notifications — use
NotificationorStopto ping chat or the desktop when work finishes. - Logging — save each event's input so you can audit behavior later.
This aligns with the idea of "harness engineering" that we covered in another article: shaping the environment (the harness) around the agent. Hooks are a strong way to lock that environment down declaratively.
Series: Getting the most out of Claude Code
Source
- Anthropic, "Hooks" (Claude Code official docs) — https://code.claude.com/docs/en/hooks
Frequently Asked Questions
What are Claude Code hooks?
Hooks are user-defined actions that run automatically at specific events in the Claude Code lifecycle, such as before or after a tool runs or when it stops. You configure them in settings.json to automate command execution, formatting, and policy enforcement.
Where are hooks configured?
Configure them as JSON under the hooks key in ~/.claude/settings.json (user-global), a project's .claude/settings.json (shared), or .claude/settings.local.json (gitignored).
Can a hook block a tool from running?
Yes. If a hook such as PreToolUse exits with code 2, the action is blocked and the stderr text is fed back to Claude as an error message. Note that exit code 1 is treated as non-blocking.