Claude Code·3 min read

How to Use Claude Code Hooks: A Practical Guide with Examples

Claude Code hooks let you run your own shell commands automatically on events like tool use and session stop. Here's how to set them up, with copy-paste examples.

By Ali Hamza

Claude Code hooks are shell commands that run automatically at specific points in a session — before or after a tool runs, when the agent stops, and more. They let you enforce rules, format code, send notifications, or block unwanted actions without asking the model to remember anything. This guide shows exactly how to configure them, with examples you can copy.

What hooks are for

The model is non-deterministic — it might forget to run your formatter or skip a check. Hooks are deterministic: they always fire on the event you bind them to. That makes them perfect for:

  • Auto-formatting files after every edit
  • Blocking edits to sensitive files (like .env)
  • Running tests or linters after changes
  • Sending a desktop notification when a long task finishes
  • Logging every command the agent runs

Where hooks live

Hooks are configured in your settings JSON — typically ~/.claude/settings.json (global) or .claude/settings.json (per project). The shape looks like this:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_FILE_PATHS\""
          }
        ]
      }
    ]
  }
}

This runs Prettier on any file Claude edits or writes — automatically, every time.

The main hook events

EventFires when…Common use
PreToolUseBefore a tool runsBlock or validate an action
PostToolUseAfter a tool succeedsFormat, lint, run tests
UserPromptSubmitYou send a messageInject context, log prompts
StopThe agent finishes respondingNotify, summarize
SessionStartA session beginsLoad environment info

Example 1: Auto-format after edits

The most popular hook. Add to .claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "npx prettier --write \"$CLAUDE_FILE_PATHS\"" }
        ]
      }
    ]
  }
}

Example 2: Block edits to protected files

Use a PreToolUse hook that exits non-zero to block an action:

#!/usr/bin/env bash
# .claude/hooks/protect-env.sh
if echo "$CLAUDE_FILE_PATHS" | grep -q "\.env"; then
  echo "Blocked: editing .env is not allowed." >&2
  exit 2   # non-zero exit blocks the tool call
fi
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/protect-env.sh" }
        ]
      }
    ]
  }
}

Example 3: Notify when a task finishes

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "osascript -e 'display notification \"Claude is done\" with title \"Claude Code\"'" }
        ]
      }
    ]
  }
}

(That command is macOS-specific; on Linux use notify-send.)

Tips for reliable hooks

  1. Keep them fast. Hooks run inline — a slow hook slows every action.
  2. Exit codes matter. A non-zero exit from a PreToolUse hook blocks the action; use that to enforce rules.
  3. Test in one project first before promoting a hook to your global settings.
  4. Log while debugging. Have the hook append to a file so you can see when it fires.

Wrapping up

Hooks turn Claude Code from a smart assistant into a governed one — your formatting, checks, and guardrails run every single time, deterministically. Start with the auto-format hook, then add guardrails as you find rough edges.

Want more? See our other Claude Code guides.


Get new AI coding guides in your inbox

Practical Claude Code tutorials and AI dev tips. No spam, unsubscribe anytime.