Skip to content

Hooks

Run your own scripts automatically at key points in a session.

Advanced11 min read

Hooks let you attach your own shell commands to specific moments in Claude Code's workflow — so things like formatting, linting, or blocking risky operations happen automatically, without you having to remember to run them.

What hooks are for

Think of hooks like git hooks (pre-commit, post-commit) but for Claude's tool calls instead of git operations. Every time Claude uses a tool — reading a file, writing code, running a shell command — hooks can fire before or after that action. You can use them to auto-format edited files, run a linter after every write, log activity, or outright block commands that match a dangerous pattern.

  • Auto-run Prettier or ESLint after Claude edits a file
  • Block shell commands that match a risky pattern (e.g. rm -rf)
  • Send a desktop notification when Claude finishes a long task
  • Inject extra context into every prompt before Claude sees it

Hook events

Each hook is tied to a named event in Claude Code's lifecycle. The most useful ones for everyday automation are:

  • PreToolUse — fires before any tool call; returning exit code 2 blocks the call entirely
  • PostToolUse — fires after a tool call succeeds; ideal for formatting or linting
  • UserPromptSubmit — fires when you submit a prompt, before Claude processes it
  • Stop — fires when Claude finishes responding (useful for notifications)
  • SessionStart — fires once when a session begins
Many more events exist
The full list includes additional events like SessionEnd, SubagentStop, PreCompact, and Notification. Start with PostToolUse and PreToolUse — they cover most automation needs.

Configure a hook

Hooks live in a settings.json file. The structure has three levels: the event name, a matcher that filters which tool triggers the hook, and the hook handler itself. You can scope hooks to all your projects (~/.claude/settings.json) or to a single project (.claude/settings.json).

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

The matcher field filters which tool calls trigger the hook. Use an exact tool name (Edit), a pipe-separated list (Edit|Write), or a regex for more complex patterns. Omit the matcher entirely to match every tool call.

Example: auto-format on save

The most common hook runs a formatter every time Claude writes or edits a file. Add this to your project's .claude/settings.json and Prettier will run automatically on every file Claude touches — no manual step needed.

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

For blocking hooks (PreToolUse), exit with code 2 to prevent the tool call — Claude will see your stderr message and stop. Exit code 0 means success. Exit code 1 is a non-blocking error and does not stop anything.

Use hooks safely

Hooks run shell commands with your full permissions
Any command in a hook executes as you, with access to everything you have access to. Never add hooks from untrusted sources (e.g. a cloned repo's .claude/settings.json) without reading them first. Treat hook scripts the same as any other code you run on your machine.
  • Exit code 2 blocks an action; exit code 1 does NOT — it is treated as a non-blocking error
  • stdout must contain only valid JSON if you return a decision object — stray output will break parsing
  • Quote file-path variables in your commands so paths with spaces do not break
  • For hard security enforcement, prefer Claude Code's permission system over a hook
Key takeaways
Hooks are shell commands that fire automatically at named lifecycle events (PreToolUse, PostToolUse, Stop, etc.). Configure them in settings.json with a matcher and a command. Use PostToolUse for formatting and linting, PreToolUse with exit 2 to block risky actions. Full reference: code.claude.com/docs/en/hooks.