Claude Code Hooks Guide: Automate Custom Workflows with Pre and Post Execution Hooks

What Are Claude Code Hooks and Why They Matter

Claude Code hooks are automated shell commands that execute in response to specific events during a Claude Code session. When Claude calls a tool (like Edit, Write, or Bash), hooks can run commands before or after the tool execution. This turns Claude Code from a standalone AI assistant into an integrated development workflow engine.

Think of hooks as CI/CD for your AI coding sessions. Just as a pre-commit hook runs linting before allowing a commit, a Claude Code PreToolUse hook can run validation before Claude writes a file. Just as a post-deploy hook sends a Slack notification, a PostToolUse hook can notify your team when Claude completes a significant change.

Common hook use cases:

  • Auto-linting: run Prettier or ESLint after every file write
  • Type checking: run tsc after TypeScript file modifications
  • Test execution: run affected tests after code changes
  • Notifications: send Slack/Discord messages when tasks complete
  • Logging: track what Claude modifies for audit purposes
  • Guardrails: block changes to protected files or directories

Hook Architecture

Hook Types

Claude Code supports three hook types:

PreToolUse: fires before a tool executes. Can block the tool if the hook fails (non-zero exit code).

PostToolUse: fires after a tool executes. Cannot block the tool (it already ran) but can trigger follow-up actions.

Notification: fires when Claude produces specific output or completes a task.

Hook Configuration Structure

Hooks are defined in settings.json (project-level at .claude/settings.json or global at ~/.claude/settings.json):

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "echo 'File modification detected'",
        "timeout": 10000
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "/path/to/your/script.sh",
        "timeout": 30000
      }
    ]
  }
}

Matcher Patterns

The matcher field determines which tool calls trigger the hook:

  • "Edit" — matches only the Edit tool
  • "Write" — matches only the Write tool
  • "Edit|Write" — matches Edit or Write
  • "Bash" — matches Bash tool calls
  • ".*" — matches all tool calls (use with caution)

Environment Variables Available to Hooks

When a hook runs, Claude Code passes context through environment variables:

  • CLAUDE_TOOL_NAME — which tool was called (Edit, Write, Bash, etc.)
  • CLAUDE_FILE_PATH — the file being modified (for Edit/Write)
  • CLAUDE_TOOL_INPUT — JSON string of the tool’s input parameters
  • CLAUDE_SESSION_ID — unique identifier for the current session

Setting Up Common Hook Workflows

Auto-Format on File Write

Run Prettier after every file modification:

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

The || true ensures the hook does not fail if Prettier encounters an unsupported file type.

Type Check After TypeScript Changes

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "if [[ \"$CLAUDE_FILE_PATH\" == *.ts ]] || [[ \"$CLAUDE_FILE_PATH\" == *.tsx ]]; then npx tsc --noEmit --pretty 2>&1 | head -20; fi",
        "timeout": 30000
      }
    ]
  }
}

This only runs tsc when TypeScript files are modified, and limits output to 20 lines to avoid flooding the conversation.

Run Affected Tests

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "if [[ \"$CLAUDE_FILE_PATH\" == *.ts ]] || [[ \"$CLAUDE_FILE_PATH\" == *.tsx ]]; then npx vitest related \"$CLAUDE_FILE_PATH\" --run 2>&1 | tail -10; fi",
        "timeout": 60000
      }
    ]
  }
}

vitest related runs only the tests that import the modified file — fast and targeted.

Protect Sensitive Files

Block Claude from modifying certain files:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "if echo \"$CLAUDE_FILE_PATH\" | grep -qE '\\.(env|pem|key)$|secrets/|credentials'; then echo 'BLOCKED: Cannot modify sensitive files' >&2; exit 1; fi",
        "timeout": 5000
      }
    ]
  }
}

A non-zero exit code from a PreToolUse hook blocks the tool execution. Claude will see the error message and adjust its approach.

Slack Notification on Task Completion

{
  "hooks": {
    "Notification": [
      {
        "matcher": ".*",
        "command": "curl -s -X POST -H 'Content-Type: application/json' -d '{\"text\":\"Claude Code completed a task in '\"$PWD\"'\"}' $SLACK_WEBHOOK_URL",
        "timeout": 5000
      }
    ]
  }
}

Git Auto-Stage After Changes

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "git add \"$CLAUDE_FILE_PATH\" 2>/dev/null || true",
        "timeout": 5000
      }
    ]
  }
}

This auto-stages every file Claude modifies, making it easy to see all changes with git diff --staged.

Advanced Hook Patterns

Conditional Hooks Based on File Type

Create a hook script that handles different file types:

#!/bin/bash
# hooks/post-edit.sh

FILE="$CLAUDE_FILE_PATH"
EXT="${FILE##*.}"

case "$EXT" in
  ts|tsx)
    npx prettier --write "$FILE" 2>/dev/null
    npx tsc --noEmit --pretty 2>&1 | head -10
    ;;
  py)
    python -m black "$FILE" 2>/dev/null
    python -m mypy "$FILE" --no-error-summary 2>&1 | head -10
    ;;
  css|scss)
    npx prettier --write "$FILE" 2>/dev/null
    npx stylelint "$FILE" 2>&1 | head -5
    ;;
  sql)
    # Validate SQL syntax
    echo "$FILE modified - manual review recommended for SQL changes"
    ;;
esac

Reference it in settings:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "bash .claude/hooks/post-edit.sh",
        "timeout": 30000
      }
    ]
  }
}

Audit Logging

Track every modification Claude makes:

#!/bin/bash
# hooks/audit-log.sh

LOG_FILE=".claude/audit.log"
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

echo "$TIMESTAMP | $CLAUDE_TOOL_NAME | $CLAUDE_FILE_PATH | session:$CLAUDE_SESSION_ID" >> "$LOG_FILE"

Dependency Check After Package Changes

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "if [[ \"$CLAUDE_FILE_PATH\" == *package.json ]]; then npm ls --depth=0 2>&1 | grep -i 'ERR\\|WARN' | head -5; fi",
        "timeout": 30000
      }
    ]
  }
}

Hook Configuration Best Practices

Keep Hooks Fast

Hooks run synchronously — a slow hook blocks Claude. Keep hooks under 10 seconds for PreToolUse and under 30 seconds for PostToolUse. If a process takes longer, run it asynchronously:

"command": "nohup bash .claude/hooks/slow-check.sh &>/dev/null &"

Handle Errors Gracefully

Always add || true or proper error handling to prevent hooks from crashing the session:

"command": "npx prettier --write \"$CLAUDE_FILE_PATH\" 2>/dev/null || true"

Use Project-Level Hooks

Store hooks in .claude/settings.json at the project root so they are:

  • Version-controlled with the project
  • Shared automatically with all team members
  • Specific to the project’s technology stack

Test Hooks Independently

Before adding a hook to settings, test it manually:

CLAUDE_FILE_PATH="src/app.ts" CLAUDE_TOOL_NAME="Edit" bash .claude/hooks/post-edit.sh

Document Your Hooks

Add a comment section to your settings.json or create a .claude/HOOKS.md explaining what each hook does and why.

Troubleshooting Common Hook Issues

Hook Does Not Fire

  • Verify the matcher pattern matches the tool name exactly
  • Check that the settings.json is in the correct location
  • Restart Claude Code after modifying settings.json

Hook Blocks Tool Execution Unexpectedly

  • PreToolUse hooks block on non-zero exit codes
  • Add || true if the hook should warn but not block
  • Check that error output goes to stderr, not stdout

Hook Output Clutters the Conversation

  • Redirect verbose output: command 2>/dev/null
  • Limit output: command | head -5
  • Send output to a log file instead: command >> .claude/hook.log 2>&1

Hook Timeout

  • Increase the timeout value (in milliseconds)
  • For slow operations, run asynchronously with nohup ... &
  • Optimize the hook script (avoid unnecessary operations)

Frequently Asked Questions

Can hooks modify Claude’s behavior?

PreToolUse hooks can block tool calls by returning a non-zero exit code. The error message (stderr) is shown to Claude, which can adjust its approach. PostToolUse hooks cannot modify what Claude already did but can trigger side effects.

Are hooks shared when pairing with Claude Code?

Hooks in .claude/settings.json (project-level) are shared with anyone who clones the repository. Hooks in ~/.claude/settings.json (global) are personal to each user.

Can I use hooks with Claude Code in VS Code?

Yes. The VS Code extension reads the same settings.json files. Hooks work identically in the terminal CLI and VS Code extension.

Do hooks work with all Claude Code tools?

Yes. The matcher can target any tool: Edit, Write, Bash, Read, Glob, Grep, and any MCP tools. Use the tool name as the matcher string.

Can hooks access the content Claude is writing?

The CLAUDE_TOOL_INPUT environment variable contains the JSON input parameters, which includes the content for Write/Edit operations. Parse it with jq or your preferred JSON tool.

Is there a performance impact from running hooks?

Yes. Each hook adds execution time to every matching tool call. Keep hooks fast (under 5 seconds) and use specific matchers to avoid running hooks unnecessarily.

Explore More Tools

Grok Best Practices for Academic Research and Literature Discovery: Leveraging X/Twitter for Scholarly Intelligence Best Practices Grok Best Practices for Content Strategy: Identify Trending Topics Before They Peak and Create Content That Captures Demand Best Practices Grok Case Study: How a DTC Beauty Brand Used Real-Time Social Listening to Save Their Product Launch Case Study Grok Case Study: How a Pharma Company Tracked Patient Sentiment During a Drug Launch and Caught a Safety Signal 48 Hours Before the FDA Case Study Grok Case Study: How a Disaster Relief Nonprofit Used Real-Time X/Twitter Monitoring to Coordinate Emergency Response 3x Faster Case Study Grok Case Study: How a Political Campaign Used X/Twitter Sentiment Analysis to Reshape Messaging and Win a Swing District Case Study How to Use Grok for Competitive Intelligence: Track Product Launches, Pricing Changes, and Market Positioning in Real Time How-To Grok vs Perplexity vs ChatGPT Search for Real-Time Information: Which AI Search Tool Is Most Accurate in 2026? Comparison How to Use Grok for Crisis Communication Monitoring: Detect, Assess, and Respond to PR Emergencies in Real Time How-To How to Use Grok for Product Improvement: Extract Customer Feedback Signals from X/Twitter That Your Support Team Misses How-To How to Use Grok for Conference Live Monitoring: Extract Event Insights and Identify Networking Opportunities in Real Time How-To How to Use Grok for Influencer Marketing: Discover, Vet, and Track Influencer Partnerships Using Real X/Twitter Data How-To How to Use Grok for Job Market Analysis: Track Industry Hiring Trends, Layoff Signals, and Salary Discussions on X/Twitter How-To How to Use Grok for Investor Relations: Track Earnings Sentiment, Analyst Reactions, and Shareholder Concerns in Real Time How-To How to Use Grok for Recruitment and Talent Intelligence: Identifying Hiring Signals from X/Twitter Data How-To How to Use Grok for Startup Fundraising Intelligence: Track Investor Sentiment, VC Activity, and Funding Trends on X/Twitter How-To How to Use Grok for Regulatory Compliance Monitoring: Real-Time Policy Tracking Across Industries How-To NotebookLM Best Practices for Financial Analysts: Due Diligence, Investment Research & Risk Factor Analysis Across SEC Filings Best Practices NotebookLM Best Practices for Teachers: Build Curriculum-Aligned Lesson Plans, Study Guides, and Assessment Materials from Your Own Resources Best Practices NotebookLM Case Study: How an Insurance Company Built a Claims Processing Training System That Cut Errors by 35% Case Study