if Field — Conditional ExecutionNew in Claude Code v2.1.85. The if field lets you specify when a hook should run using permission rule syntax. If the command doesn't match, the hook process is never spawned.
Without if, every Bash command triggers every hook with "matcher": "Bash". Running ls spawns your git-only hook. Running echo spawns your npm-only hook. With 10 hooks, a simple ls spawns 10 processes.
{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "~/.claude/hooks/branch-guard.sh"
}]
}
branch-guard.sh runs for every Bash command. It checks internally and exits early for non-git commands. But the process is already spawned.
{
"matcher": "Bash",
"hooks": [{
"type": "command",
"if": "Bash(git push *)",
"command": "~/.claude/hooks/branch-guard.sh"
}]
}
branch-guard.sh only runs when the command starts with git push. Zero process spawning for ls, echo, npm test.
The if field uses the same syntax as permissions.allow / permissions.deny:
Bash(git *) — git commands
Bash(npm install *) — npm install
Bash(rm *) — rm commands
Edit(*.py) — Python file edits
Write(*.json) — JSON file writes
if inside the hook handler (same level as type and command), not at the matcher levelif preserves backward compatibility| Command | Before (10 hooks) | After (with if) |
|---|---|---|
ls | 10 processes | 0 |
git push | 10 processes | 1-2 |
npm test | 10 processes | 1 |
Get 406 hook examples with if-field documentation:
npx cc-safe-setup
9,677 tests 605 examples v2.1.85 ready
cc-safe-setup · 628 hooks · 9,677 tests · GitHub
Learn more: Production Guide · All Tools