Advanced

Mastering Claude Code Hooks: Auto-Format, Auto-Test, and More

Learn how to set up auto-formatting and auto-testing with Claude Code hooks. Includes practical configuration examples and real-world use cases.

What Are Hooks?

Claude Code hooks let you automatically run custom commands before or after specific actions. You can set up auto-formatting after file saves, auto-run tests after code changes, and much more.

Hooks are defined in .claude/settings.json and execute shell commands when Claude Code performs certain operations.

Hook Types

Claude Code supports hooks at the following event points:

Hook EventWhen It Fires
PreToolUseBefore a tool is executed
PostToolUseAfter a tool is executed
NotificationWhen a notification is sent
StopWhen Claude Code finishes responding

Basic Configuration

Hooks are defined in the hooks field of .claude/settings.json:

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

Configuration Structure

  • matcher: A regex pattern that matches the tool name triggering the hook
  • command: The shell command to execute

Use Case 1: Auto-Formatting

Automatically run Prettier after any file edit:

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

With this setup, every time Claude Code creates or edits a file, Prettier formats it automatically. Your team’s coding style stays consistent without any manual effort.

Use Case 2: Auto-Linting

Here’s how to integrate ESLint auto-fix:

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

The || true suffix prevents lint errors from causing the hook to fail, allowing Claude Code to continue processing.

Use Case 3: Auto-Testing on Change

Automatically run related tests after file edits and feed back the results:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "command": "npx vitest related \"$CLAUDE_FILE_PATH\" --run 2>&1 | tail -20"
      }
    ]
  }
}

Vitest’s related option runs only the tests relevant to the changed file, giving you much faster feedback than a full test suite.

Use Case 4: Auto Type-Checking

Run TypeScript type checking after file changes:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "command": "npx tsc --noEmit 2>&1 | head -30"
      }
    ]
  }
}

Use Case 5: Blocking Dangerous Commands

A PreToolUse hook that prevents execution of risky commands:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -qE 'rm -rf|drop table|git push.*force'; then echo 'BLOCKED: Dangerous command detected' >&2; exit 1; fi"
      }
    ]
  }
}

When the hook exits with code 1, Claude Code skips the tool execution entirely.

Use Case 6: Completion Notifications

Send a notification when Claude Code finishes a task:

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "command": "notify-send 'Claude Code' 'Task completed' 2>/dev/null; echo 'Done'"
      }
    ]
  }
}

On macOS, you can use:

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "command": "osascript -e 'display notification \"Task completed\" with title \"Claude Code\"'"
      }
    ]
  }
}

Combining Multiple Hooks

In real projects, combining several hooks is the most effective approach:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -qE 'rm -rf /'; then exit 1; fi"
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "command": "npx prettier --write \"$CLAUDE_FILE_PATH\" 2>/dev/null || true"
      },
      {
        "matcher": "Write|Edit",
        "command": "npx eslint --fix \"$CLAUDE_FILE_PATH\" 2>/dev/null || true"
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "command": "echo '✓ Task completed'"
      }
    ]
  }
}

Tips for Writing Hooks

1. Keep Execution Time Short

Hooks run synchronously, so heavy operations will slow down Claude Code’s responses. Use options like related to narrow test scope, and head or tail to trim output.

2. Handle Errors Gracefully

A hook that exits with an error can disrupt Claude Code’s workflow. Use || true and 2>/dev/null to keep things safe.

3. Use Environment Variables

Take advantage of the environment variables available in hooks. $CLAUDE_FILE_PATH gives you the path of the file being operated on.

Conclusion

Hooks let you heavily customize Claude Code’s workflow. The combination of auto-formatting and auto-testing is especially powerful for maintaining code quality. Start with Prettier auto-formatting and build from there.

#Claude Code #hooks #automation #formatting #testing