Step 1: Claude Code prepares an action
For example, Claude Code is about to write to a file using the Edit tool
Step 2: PreToolUse hook runs
Your configured command executes. It can approve, block, or modify the action
Step 3: Action executes
If the hook approved it (or no PreToolUse hook exists), the tool call proceeds normally
Step 4: PostToolUse hook runs
After the action completes, your post-action command runs. Auto format, lint, notify, etc.
Start with auto formatting
The highest impact hook for most teams is a PostToolUse hook that runs your formatter (Prettier, Black, gofmt, etc.) after every file edit. This ensures Claude Code's output always matches your code style, eliminating formatting nits from code reviews. Add it to your project's
.claude/settings.json so the whole team benefits.Example: Auto-format TypeScript files after edits
In your .claude/settings.json, add a PostToolUse hook that runs Prettier whenever Claude Code edits a file:
{
"hooks": {
"PostToolUse": [{
"matcher": "Edit|Write",
"command": "npx prettier --write $CLAUDE_FILE_PATH"
}]
}
}Example: Block writes to generated files
A PreToolUse hook that prevents Claude Code from modifying auto-generated files:
{
"hooks": {
"PreToolUse": [{
"matcher": "Edit|Write",
"command": "echo $CLAUDE_FILE_PATH | grep -q 'generated/' && exit 1 || exit 0"
}]
}
}| Hook Type | When it runs | Common uses | |
|---|---|---|---|
| PreToolUse | PreToolUse | Before a tool call executes | Block writes to protected files, validate commands, require approval |
| PostToolUse | PostToolUse | After a tool call completes | Auto format code, run linters, update lock files, log changes |
| Notification | Notification | When Claude Code sends a notification | Desktop alerts, Slack messages, sound effects, email notifications |
Environment variables in hooks
Hooks receive context through environment variables.
$CLAUDE_FILE_PATH contains the path of the file being modified. $CLAUDE_TOOL_NAME contains the name of the tool being called (Edit, Write, Bash, etc.). Your hook commands can use these to make targeted decisions, like only formatting TypeScript files or only validating Bash commands that match certain patterns.