Hooks

TL;DR

Hooks are shell commands you configure in settings.json that automatically run before or after Claude uses specific tools. Use them for linting, formatting, notifications, or blocking dangerous operations.

How Hooks Work (Visual)

This sequence diagram shows exactly what happens when Claude tries to edit a file and hooks are configured. Notice the two checkpoints — one before, one after.

1
You send a message
"Edit this file for me"
PreToolUse Hook fires
Your script runs before the tool
Allow
Exit code 0 → proceed
or
Block
Non-zero → tool skipped
2
Edit Tool executes
Claude makes the file change
PostToolUse Hook fires
e.g., auto-format with Prettier
3
Claude continues
"Done! I've edited the file."
Explain Like I'm 12
Imagine you have a helper who edits your code. Hooks are like checkpoint guards — one checks with you BEFORE the edit happens ("Are you sure?"), and another runs AFTER ("Let me auto-format that for you"). You set up the rules once, and they run automatically every time.

What are Hooks?

At the simplest level, hooks are shell commands that Claude Code runs automatically at specific moments. You don't invoke them manually. You configure them once, and they fire every time the matching event happens.

Think of them like Git hooks, but for Claude Code's tool usage. Every time Claude is about to use a tool (like editing a file or running a command), your hook gets a chance to intervene. And every time a tool finishes, another hook can react to the result.

This gives you a powerful automation layer on top of Claude Code. You stay in control without having to babysit every action.

Hook Types

There are exactly two hook types. That's it. Simple.

PreToolUse

Fires before Claude executes a tool. This is your chance to inspect, validate, or block the action. If your hook exits with a non-zero code, the tool is blocked and Claude gets told "nope."

Use cases: checking if a file should be edited, validating a command before it runs, logging what's about to happen.

PostToolUse

Fires after Claude executes a tool. The action already happened, so you can't block it — but you can react to it. Run formatters, send notifications, update logs, whatever you need.

Use cases: auto-formatting code after edits, running tests after file changes, sending a Slack message after a commit.

Configuration

Hooks live in your settings.json file. The structure is straightforward: you have a top-level "hooks" key, and under it you define arrays for PreToolUse and PostToolUse.

Each hook entry has two fields:

  • matcher — which tool this hook applies to (e.g., "Edit", "Bash")
  • command — the shell command to run

Here's a complete example:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit",
        "command": "echo 'About to edit...'"
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit",
        "command": "npx prettier --write $FILE"
      }
    ]
  }
}

That's the whole thing. When Claude is about to edit a file, it echoes a message. After the edit, it auto-formats with Prettier. Clean and declarative.

The Matcher

The matcher field tells Claude Code which tool triggers the hook. You can match specific tools by name:

  • "Edit" — matches the Edit tool
  • "Bash" — matches the Bash tool
  • "Read" — matches the Read tool

You can also use glob patterns for broader matching:

# Match any tool
"*"

# Match tools starting with "File"
"File*"

If a hook's matcher matches the tool Claude is about to use (or just used), the hook fires. Otherwise, it's silently skipped. No overhead, no noise.

Real-World Examples

Let's look at some practical hooks you might actually use in a real project.

1. Auto-format with Prettier after edits

This is probably the most common hook. Every time Claude edits a file, Prettier cleans it up automatically.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit",
        "command": "npx prettier --write $FILE"
      }
    ]
  }
}

2. Run linter before Bash commands

Catch problems before they happen. This hook runs ESLint on your source before Claude executes any bash command.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "npx eslint src/ --quiet"
      }
    ]
  }
}

3. Block certain file patterns from being edited

Don't want Claude touching your lock files or generated code? Block it.

# In a script referenced by your hook:
if echo "$FILE" | grep -qE '(\.lock|\.generated\.)'; then
  echo "Blocked: Cannot edit lock or generated files"
  exit 1
fi

Set the hook's command to run that script, and any edit to a matching file gets blocked (because of the non-zero exit code).

4. Send Slack notification after commits

Want your team to know when Claude commits something? Hook into it.

# post-commit-notify.sh
curl -X POST -H 'Content-type: application/json' \
  --data '{"text":"Claude just made a commit!"}' \
  "$SLACK_WEBHOOK_URL"
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "command": "bash ./scripts/post-commit-notify.sh"
      }
    ]
  }
}

Tips & Gotchas

Things to keep in mind

  • Hooks run synchronously. Claude waits for your hook to finish before moving on. Keep them fast, or Claude will feel slow.
  • Non-zero exit codes block the tool (PreToolUse only). If your PreToolUse hook exits with code 1 (or any non-zero), the tool is blocked. PostToolUse hooks don't block anything — the action already happened.
  • Environment variables are available. Hooks receive context like $FILE and $TOOL_NAME so you can write conditional logic.
  • Test your hooks manually first. Run the command in your terminal before putting it in settings.json. Debug once, not during a Claude session.
  • Order matters. If you have multiple hooks for the same matcher, they run in the order they appear in the array.

Test Yourself

Q: Where do you configure hooks?

In settings.json, under the "hooks" key.

Q: What are the two hook types?

PreToolUse and PostToolUse. PreToolUse fires before a tool runs, PostToolUse fires after.

Q: Can a PreToolUse hook block a tool from running?

Yes, by returning a non-zero exit code. If the hook exits with code 1, the tool is blocked.

Q: Give one real-world use case for hooks.

Auto-formatting code after edits (e.g., running Prettier), running linters before commands, blocking edits to certain files, or sending notifications after commits.