How to set up Claude Code hooks, with examples
Hooks are shell commands that Claude Code runs on its own at set moments, like right before it uses a tool or right after it edits a file. You can use them to format code, send yourself an alert, or stop a risky command. This guide shows where hooks go, the events you'll use most, what exit codes mean, two examples from Anthropic's docs, and the security rules. Everything here comes from Anthropic's hooks guide and hooks reference, read on October 15, 2026.
Not affiliated with or endorsed by Anthropic. Check Anthropic's current docs before relying on these details.
Our tool page: Claude Code
Where hooks go
Hooks live in a "hooks" object in a Claude Code settings file. Where you put them sets who they apply to. From the reference:
| Location | Scope | Shareable |
|---|---|---|
~/.claude/settings.json | All your projects | No, local to your machine |
.claude/settings.json | Single project | Yes, can be committed to the repo |
.claude/settings.local.json | Single project | No, gitignored when Claude Code saves a setting to it |
The reference says to type /hooks "in Claude Code to open a read-only browser for your configured hooks." It labels each hook with where it came from.
The events you'll use most
The reference lists many events. Two cover most needs:
PreToolUse: "Before a tool call executes. Can block it".PostToolUse: runs after a tool call. The guide notes that "PostToolUse hooks can't undo actions since the tool has already executed."
Each hook has a matcher that picks which tools it runs for, like "Edit|Write" or "Bash".
What exit codes mean
Your hook tells Claude Code what happened through its exit code. From the reference:
- Exit 0: "Exit 0 means success, and is the intended exit code when you print JSON for structured control."
- Exit 2: "Exit with code 2 to block the action. On events that can block, Claude Code stops the action: a PreToolUse hook blocks the tool call, for example, and a UserPromptSubmit hook rejects the prompt." The message Claude sees is your hook's stderr.
- Any other code: "When your hook exits with a code other than 0 or 2 and prints plain text or nothing to stdout, the run is a non-blocking error."
The detail that matters most: "Without valid JSON on stdout, Claude Code treats exit code 1 as a non-blocking error, even though 1 is the conventional Unix failure code. If your hook is meant to enforce a policy, use exit 2."
Example: format files after every edit (NOT TESTED)
This is the guide's Prettier example. It uses PostToolUse with an Edit|Write matcher. The guide says to add it to .claude/settings.json in your project root:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
The guide notes: "When the hook succeeds, Claude Code shows nothing in the conversation." To check that it worked, look for the edited file being reformatted.
Example: block rm commands (NOT TESTED)
This script is from the reference. Registered as a PreToolUse hook on Bash, it blocks commands that start with rm by exiting 2. Every other command goes through the normal permission flow:
#!/bin/bash
# Reads JSON input from stdin, checks the command
input=$(cat)
command=$(jq -r '.tool_input.command' <<<"$input")
if [[ "$command" == rm* ]]; then
echo "Blocked: rm commands are not allowed" >&2
exit 2 # Blocking error: tool call is prevented
fi
exit 0 # No decision: the normal permission flow applies
The reference also explains why exit 0 here doesn't mean "allow": "Exit code 0 with no output means the hook has no decision to report, so the tool call continues through the normal permission flow. The hook can deny the call, but staying silent doesn't approve it."
Security: read this before adding any hook
The reference's disclaimer, word for word: "Command hooks execute shell commands with your full user permissions. They can modify, delete, or access any files your user account can access. Review and test all hook commands before adding them to your configuration."
Its security checklist:
- "Validate and sanitize inputs: never trust input data blindly"
- "Always quote shell variables: use "$VAR" not $VAR"
- "Block path traversal: check for .. in file paths"
- "Use absolute paths: specify full paths for scripts"
- "Skip sensitive files: avoid .env, .git/, keys, etc."
Watch out for other people's repos. A project's .claude/settings.json can hold hooks, and it can be committed. In an interactive session, the reference says Claude Code holds hooks back "until you accept the workspace trust dialog for the folder." But in -p or SDK sessions, it "never shows the dialog and treats the folder as trusted, so hooks committed in a repository's .claude/settings.json run in a folder you've never trusted." Before you run claude -p on a repo you didn't write, the reference says to "review its .claude/ settings files, start with --bare, or turn hooks off for that run".
Hooks are an extra safety layer, not a full one. For keeping secrets out of reach in the first place, see our guide to keeping secrets safe with AI coding tools. For project instructions, see our AGENTS.md and CLAUDE.md guide, and for connecting outside tools, our MCP setup guide.
Not affiliated with or endorsed by Anthropic. Check Anthropic's current docs before relying on these details.