In this categoryClaude Code · 45
- How to Install Claude Code on macOSStart
- How to Install Claude Code on Windows Natively (No WSL)
- How to Install Claude Code on Windows with WSL
- How to Start Your First Project with Claude Code
- How to Create a CLAUDE.md File for Your Project
- How to Configure Claude Code with settings.json
- How to Layer Global, Project, and Local CLAUDE.md Files
- How to Set Up a Permissions Allowlist in Claude Code
- How to Use Claude Code Inside VS Code
- How to Add a Custom Slash Command in Claude Code
- How to Create a Custom Slash Command in Claude Code
- How to Pass Arguments to a Claude Code Slash Command
- How to Use Claude Code Slash Commands (Full List + Examples)
- How to Add Custom Slash Commands to Claude Code (Arguments + Frontmatter)
- How to Fix command not found After Installing Claude Code
- How to Add Any MCP Server to Claude Code
- How to Connect an MCP Server to Claude Code
- Best MCP Servers for Coding in 2026
- How to Add a Filesystem MCP Server to Claude Code
- How to Connect the GitHub MCP Server to Claude Code
- How to Create Your First Subagent in Claude Code
- Context7 Alternatives: MCP Servers for Up-to-Date Docs
- How to Manage MCP Server Scopes in Claude Code
- How to Restrict the Tools a Subagent Can Use
- How to Debug a Failing MCP Server in Claude Code
- How to Generate Unit Tests for a Function with Claude Code
- How to Refactor a Long Function Safely with Claude Code
- How to Debug a Failing Test with Claude Code
- How to Review Your Own Diff Before Committing with Claude Code
- How to Rename a Symbol Across the Codebase with Claude Code
- How to Review a Teammate's Pull Request with Claude Code
- How to Raise Test Coverage on a Specific File with Claude Code
- How to Debug a Runtime Error from a Stack Trace with Claude Code
- How to Add an Integration Test for an API Route with Claude Code
- How to Teach Claude Code Your Test Conventions with CLAUDE.md
- How to Find Which Commit Broke a Test with Claude Code
- How to Set Up Hooks for Automation in Claude Code
- Claude Code Hooks Explained: Events, Config, and Blocking
- How to Block Risky Commands with a Claude Code Hook
- How to Auto-Format Files After Edits with a PostToolUse Hook
- How to Get a Notification When Claude Code Finishes a Task
- How to Give a Coding Agent a Clear Stop Condition
- How to Make an Agent Plan Before It Edits
- How to Keep an Agent Session Focused by Clearing Context
- How to Pin a Specific Model for One Project
Claude Code Hooks Explained: Events, Config, and Blocking
How Claude Code hooks work end to end: the lifecycle events you can hook, the settings.json structure, and how a hook blocks or approves an action.
Hooks are shell commands Claude Code runs automatically at points in its lifecycle: before a tool runs, after an edit, when you submit a prompt, when the session ends. They let you enforce rules, auto-format code, send notifications, or block dangerous actions without asking the model to remember anything. This guide is the map: which events exist, how to configure one, and how a hook can approve or block an action.
What you need
- Claude Code installed in a project
- Comfort editing JSON and a small shell script
- About 10 minutes
The events you can hook
There are many hook events; these are the ones most setups actually use. Each fires at a specific moment and receives JSON about what is happening on stdin.
| Event | Fires when |
|---|---|
| SessionStart | A session begins or resumes |
| UserPromptSubmit | You submit a prompt, before Claude processes it |
| PreToolUse | Before a tool call executes (can block it) |
| PostToolUse | After a tool call succeeds (for example, run a formatter) |
| Notification | Claude Code sends a notification |
| SubagentStop | A subagent finishes |
| Stop | Claude finishes responding |
| PreCompact | Before context is compacted |
| SessionEnd | A session terminates |
Step 1: Add a hook in settings.json
Hooks live in a JSON settings file: .claude/settings.json for the project, or ~/.claude/settings.json for you personally. Each event holds an array of matcher groups; each group runs one or more command handlers. The matcher filters which tools trigger the hook.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/format.sh"
}
]
}
]
}
}This runs format.sh after every Edit or Write. The matcher takes a single tool name, a pipe-separated list like Edit|Write, a regex, or * (or omit it) to match everything.
Step 2: Read the input in your script
The hook command receives a JSON object on stdin describing the event: the session id, the tool name, and the tool input. Parse it with jq to decide what to do.
#!/bin/bash
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive command blocked by hook"
}
}'
else
exit 0
fiStep 3: Block or approve an action
A hook controls the flow two ways. The simple way is exit codes: exit 0 allows and (optionally) prints JSON, exit 2 blocks and sends stderr back to Claude as the reason, any other code is a non-blocking error. The precise way is JSON on stdout with exit 0.
- PreToolUse: return hookSpecificOutput with permissionDecision set to allow, deny, or ask
- UserPromptSubmit, PostToolUse, Stop, SubagentStop: return a top-level decision of block plus a reason
- Exit code 2 from any command hook: blocks the action, stderr becomes the reason
FAQ
Where should hooks be defined?
Put team-wide hooks in the project .claude/settings.json so everyone shares them, and personal hooks in ~/.claude/settings.json. Project and user hooks both run; they do not replace each other.
How do I stop a hook from blocking me?
A blocking hook uses exit code 2 or a deny decision. If a hook is getting in your way, check its script's condition, or remove its entry from settings.json. Run the script by hand with sample JSON on stdin to see exactly what it returns.
Related guides
How to Block Risky Commands with a Claude Code Hook
Use a PreToolUse hook to inspect Bash commands before they run and deny dangerous ones automatically.
How to Auto-Format Files After Edits with a PostToolUse Hook
Run Prettier automatically every time Claude Code writes or edits a file using a PostToolUse hook.
How to Get a Notification When Claude Code Finishes a Task
Use a Stop hook to fire a desktop or sound notification the moment Claude Code finishes responding.
Watch related tutorials
5:42
1:42:18
28:14
41:09
9:47
8:23New guides in your inbox
Fresh step-by-step how-to guides as we publish them. One email a week, no more.