FEATURE

Claude Code Hooks Explained: Automate Your Workflow

Claude code hooks let you run your own shell commands at key lifecycle events for deterministic formatting, guardrails, and notifications. Here's how they actually work.

By Ian MacCallum··7 min read

Claude code hooks are user-defined shell commands that Claude Code runs automatically at specific points in its lifecycle — for example, before or after a tool runs, or when the assistant finishes responding. Where the model decides what to do, hooks let you decide what must happen deterministically every single time: auto-formatting edited files, blocking a dangerous command, or pinging a notification channel. If you want guardrails that don't depend on the model remembering an instruction, hooks are the feature to reach for. (Claude Drops is an independent project and is not affiliated with Anthropic; always confirm exact syntax against the official Claude Code docs.)

What are Claude code hooks?

A hook is a command that the Claude Code harness executes in response to a lifecycle event. You register hooks in your settings (a settings.json file), mapping an event to one or more commands. When that event fires, Claude Code shells out and runs your command, passing structured JSON about the event on standard input. The key insight is that hooks are executed by the harness itself, not by the model — so they fire reliably regardless of what the model chooses to do. That makes them ideal for policy you can't afford to leave to chance.

Think of it as the difference between asking a teammate to "please run the linter after you edit" and wiring the linter into a pre-commit step. The first depends on memory and goodwill; the second happens every time. Hooks bring that same determinism to an AI coding agent. For a broader tour of the vocabulary around the tool, the Claude Code glossary is a useful companion.

Hooks run with your user permissions on your machine. They can read your files, run arbitrary commands, and access your environment — so treat any hook config like code you'd review before merging, and never paste in a hook you don't fully understand.

Why hooks matter for Claude Code automation

The whole point of Claude Code automation is to offload repetitive work — but you still want control over the parts that have to be correct. Hooks give you that control without micromanaging the model. A few high-value reasons developers reach for them:

  • Deterministic guardrails. Block edits to protected paths (like .env or a production migration directory) or reject obviously destructive shell commands before they run.
  • Consistent formatting. Run Prettier, gofmt, Black, or your formatter of choice immediately after a file is edited, so the codebase stays clean without the model having to remember.
  • Notifications. Send a desktop alert, Slack message, or push notification when Claude finishes a long task or is waiting on your input.
  • Auditing and logging. Append a record of every tool call to a log file for later review — handy in teams that need traceability.
  • Context injection. Surface project-specific reminders or environment details at the right moment so the model has what it needs.

Because these behaviors are wired into the harness, they compose nicely with other features. Pair a formatting hook with custom slash commands and you get a workflow where one command kicks off a task and hooks keep the output tidy and safe automatically.

Lifecycle events you can hook into

Claude Code exposes a number of lifecycle events, and the window around a tool call is the most common place to attach behavior. The general categories look like this:

Event categoryWhen it firesTypical use
Before a tool runsRight before Claude executes a tool such as a file edit or a shell commandValidate or block the action; inspect arguments
After a tool runsImmediately after a tool completesFormat edited files; log the result
On notificationWhen Claude needs your attention or is waitingSend a desktop or push alert
On stop / finishWhen the assistant finishes its responseRun a final check, summary, or cleanup
On session startAt the beginning of a sessionInject context or set up the environment
Exact event names, the matcher syntax for targeting specific tools, and the JSON schema passed to your command can change over releases. Don't hardcode names from memory — check the current spec in the official docs and the changelog before relying on them.

Anthropic regularly refines hook events and their payloads, so it's worth watching the release notes. You can follow every change on the Claude Code changelog or read the raw source at the CHANGELOG.md on GitHub.

How to configure Claude code hooks in settings.json

Hooks live in your Claude Code settings. You can scope them at different levels — user-wide, per-project, or in a local (un-committed) override — which lets a whole team share guardrails while individuals keep personal tweaks private. The structure nests an event to a list of matcher groups, each containing the hooks to run. Conceptually it looks like the shape below; treat the exact field names as illustrative and verify them against the docs.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "prettier --write \"$file_path\" 2>/dev/null || true"
          }
        ]
      }
    ]
  }
}

The matcher targets which tools the hook applies to, so a formatting hook can fire only for file edits and ignore everything else. Your command receives event details on stdin as JSON, which you can parse with jq or a small script to read the file path, tool name, and arguments (for an edit, the path typically lives at tool_input.file_path). For hooks that can block an action, the exit code controls whether Claude Code proceeds — a blocking exit stops the tool from running.

Keep hook commands fast and fail-safe. Append `|| true` to non-critical hooks so a missing tool or transient error never derails your session, and reserve blocking behavior for the checks that genuinely must stop the action.

Practical Claude code hooks examples

Here are a few patterns worth stealing. They're illustrative — adapt the event names and payload parsing to the current schema in the docs.

Auto-format on every edit

Attach a formatter to the after-tool event (the post-tool-use event), matched to edit and write tools. Now any file Claude touches comes back formatted to your project's standard, without a single reminder in your prompt.

Block edits to protected files

On the before-tool event (the pre-tool-use event), read the target path from stdin and exit with a blocking status if it matches something sensitive like a secrets file or a lockfile. This is a deterministic safety net that doesn't rely on the model honoring an instruction.

#!/usr/bin/env bash
# Reject edits to protected paths. Reads the tool-input JSON on stdin.
# In Claude Code, exit code 2 signals a blocking error on PreToolUse.
input=$(cat)
path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
case "$path" in
  *.env|*/secrets/*|*pnpm-lock.yaml)
    echo "Blocked: $path is protected" >&2
    exit 2 ;;
esac
exit 0
Exit codes are meaningful to the harness: exit 0 lets the action proceed, while a blocking exit code (check the docs for the current value — exit 2 at the time of writing) stops a PreToolUse action and surfaces your stderr message. Other non-zero codes are treated as non-blocking errors.

Notify when a task finishes

On the stop or notification event, fire a desktop notification or push so you can step away during long runs and get pulled back when Claude needs you. This pairs especially well with agent workflows where a task might run for several minutes.

Hooks vs. instructions vs. permissions

Claude Code gives you a few overlapping ways to shape behavior, and choosing the right one keeps your setup clean:

  • Project instructions (CLAUDE.md): guidance the model reads and usually follows — great for style and conventions, but not guaranteed.
  • Permissions / allowlists: control which tools and commands are allowed at all, prompting or denying as needed.
  • Hooks: deterministic code that runs on lifecycle events — the right tool when something must happen or must not happen, every time, regardless of the model's choices.

A good mental model: use instructions for preferences, permissions for what's allowed, and hooks for what's enforced. The three layers complement each other rather than compete.

Getting started safely

  1. Start with a single, harmless hook — like logging tool calls to a file — to confirm the wiring works.
  2. Add a formatting hook scoped to edit tools and watch your diffs come back clean.
  3. Introduce a blocking hook only once you've tested its logic in isolation, so a bug doesn't lock you out of legitimate edits.
  4. Commit team-wide hooks to your project settings and keep experimental ones in your local settings file.
  5. Re-check the docs after each Claude Code update, since event names and payloads can change.

Hooks are one of the highest-leverage features for turning Claude Code from a smart assistant into a dependable part of your toolchain. Start small, layer in guardrails as you trust them, and let determinism handle the parts that can't be left to chance. To stay current on new hook events and other improvements, browse the Claude Code changelog — or grab the Claude Drops app from the home page to get a push notification the moment a new release ships.

IM

Ian MacCallum

Maintainer, Claude Drops

Ian builds Claude Drops and reads every Claude Code release so you don't have to. He writes plain-English guides to Claude Code's features, drawing directly from the official changelog and documentation.

Stay on top of Claude Code

Get notified the moment a new version ships, and browse the full Claude Code changelog.

Get Claude Drops

FAQ

Frequently asked questions

What are Claude code hooks?+
Claude code hooks are user-defined shell commands that the Claude Code harness runs automatically at lifecycle events — such as before or after a tool runs, when a notification fires, or when the assistant finishes responding. Because the harness executes them rather than the model, they run deterministically every time, which makes them ideal for guardrails, formatting, and notifications.
Where do I configure Claude code hooks?+
Hooks are configured in your Claude Code settings (a settings.json file). You can define them at the user level for all projects, at the project level to share with your team, or in a local settings file for personal, un-committed overrides. Each entry maps a lifecycle event to one or more commands, grouped by a matcher. Check the official Claude Code docs for the current field names and schema.
Can a hook block Claude Code from running a command?+
Yes. A hook attached to a before-tool (PreToolUse) event can inspect the tool's arguments — passed as JSON on standard input — and stop the action by exiting with a blocking status (exit code 2 at the time of writing), with your message printed to stderr. This is how developers block edits to sensitive files or reject destructive shell commands deterministically, without relying on the model to follow an instruction. Confirm the exact exit-code behavior in the docs, as it can change.
What can I use Claude code hooks for?+
Common uses include auto-formatting files after every edit, blocking changes to protected paths like secrets or lockfiles, sending desktop or push notifications when a task finishes, logging tool calls for auditing, and injecting project context at session start. Anything you want to happen reliably, every time, is a good candidate for a hook.
Are hook event names and schemas stable across Claude Code versions?+
Not entirely. Anthropic refines hook events, matcher syntax, and the JSON payloads over time, so it's best not to hardcode names from memory. Verify the current specification in the official Claude Code documentation and watch the changelog after each update to catch breaking changes early.