โ† Back

Hooks in Claude

๐Ÿ“– See also: Glossary: Claude Code Building Blocks for a full breakdown of all the pieces (skills, hooks, subagents, MCP, plugins, etc.)

Hooks in Claude Code are a way to run custom scripts or commands automatically at specific points during Claude's workflow. Think of them as "triggers" that fire before or after certain events.

There are a few key hook points:

PreToolUse โ€” runs before Claude executes a tool (like writing a file, running a bash command, etc.). You can use this to validate, block, or modify what Claude is about to do.

PostToolUse โ€” runs after a tool has been executed. Useful for things like auto-formatting code after Claude writes it, or running linters.

Notification โ€” runs when Claude sends a notification (like when a long task finishes).

Stop โ€” runs when Claude is about to end its turn. You can use this to force Claude to keep going if certain conditions aren't met.

Hooks are defined in your Claude Code settings file (.claude/settings.json in your project or ~/.claude/settings.json globally). Here's a simple example:

json

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

This hook says: "Every time Claude writes a file, run Prettier on it automatically."

Key concepts:

The matcher filters which tool triggers the hook (e.g., only write_file, only bash, etc.). If you omit it, the hook runs for every tool use.

The command is the shell command that gets executed. Claude passes context through environment variables like $CLAUDE_FILE_PATH or $CLAUDE_TOOL_INPUT.

The hook script receives a JSON payload via stdin with details about the tool call, and it can return JSON to stdout to modify behavior (like blocking the action or transforming the output).

Two concepts are worth keeping in mind for product designers:

The TypeScript hook principle โ€” The idea of "after every change, validate automatically" does apply to design token work. Imagine a hook that after Claude edits a token file, checks that values follow your spacing scale or that colors are in the correct format for your design system. Same concept, different application.

The duplication hook principle โ€” If you use Claude Code to generate components, a hook that checks "does a similar component already exist?" before creating a new one could prevent inconsistencies in your design system.

Would telling Claude in natural language to check for duplicates create a hook?

No. They are two different things.

Telling Claude in natural language (in chat or in your CLAUDE.md) to "always check if a similar component exists before creating a new one" is just an instruction. Claude will try to follow it, but it can forget during long conversations or complex tasks. It depends on context.

A hook is code that runs automatically at system level, outside of the conversation. It fires every time the condition is met, regardless of what Claude is thinking or doing.

Natural language instruction โ€” Claude tries to follow it, but may forget. It's a suggestion.

Hook โ€” Runs always, no exceptions. It's a forced mechanism that doesn't depend on Claude "remembering."

For design system work, the natural language instruction in CLAUDE.md is probably more than enough. Hooks make more sense in large projects where the cost of an error (like duplicating a database query) is high and you can't afford for it to be forgotten.

Example:

The goal: Prevent Claude Code from reading your .env file, which typically contains API keys, passwords, and other secrets.

How it works:

The settings.local.json defines a PreToolUse hook with the matcher "Read|Grep". This means: every time Claude tries to use the Read or Grep tool (i.e., look at a file's contents), run read_hook.js before the action happens.

Inside read_hook.js, the script checks if the file Claude is trying to read contains .env in the path. If it does, two things happen: it logs a warning to the console ("Claude is trying to read the .env file!"), and it exits with code 2. That exit code 2 is the key โ€” it tells Claude Code to block the action entirely.

So the flow is:

Claude wants to read .env โ†“ PreToolUse hook fires (because "Read" matches the matcher) โ†“ read_hook.js runs โ†“ Checks the file path โ†’ contains ".env" โ†“ exit(2) โ†’ ACTION BLOCKED

Why this matters for your work: Think of it like a security guard at the door. Before Claude can touch any file, the hook inspects what it's about to do. If it's something sensitive (like reading secrets), the guard stops it. This is especially useful in real projects like El Confidencial where your .env might have database credentials, API tokens, or other things that should never be exposed in a Claude Code session.

The exit codes to remember: exit(0) means "all good, let Claude proceed," and exit(2) means "block this action."

Other hook types โ€” what's relevant for a designer?

Beyond PreToolUse and PostToolUse, there are other hooks: Notification, Stop, SubagentStop, PreCompact, UserPromptSubmit, SessionStart, and SessionEnd.

The most interesting ones:

Stop hook โ€” Runs when Claude finishes responding. Could be used to automatically run a checklist after Claude completes a task, like "did the output follow the design system rules?"

UserPromptSubmit hook โ€” Runs before Claude even processes your message. Could be used to automatically inject context, like always prepending "remember to follow the design system guidelines" to every prompt.

The debugging trick โ€” Using jq . > post-log.json as a hook command writes all the input data to a file so you can inspect it. It's like console.log for hooks โ€” simple but effective when something isn't working.

The rest (SubagentStop, PreCompact, SessionStart/End, the stdin structure details) is more relevant for developers building complex automations. Good to know they exist, but not something you'd use day to day as a designer.