Skip to content

What Are Claude Code Stop Hooks and How Do They Work?

Problem

I kept forgetting to run the linter after Claude edited my code. Every time Claude finished writing a feature, I’d move on to the next task, only to discover linting errors later during CI. The back-and-forth was exhausting: “Claude, run the linter. Oh, there are errors. Fix them. Run it again.”

I needed a way to automate these follow-up actions. I didn’t want to manually request quality checks after every single Claude action.

Environment

  • Claude Code CLI
  • Node.js project with ESLint and Prettier configured
  • macOS development environment

What Happened

I discovered Claude Code hooks while browsing Reddit. Someone mentioned they use “stop hooks” to automatically run linters after code changes. This sounded exactly like what I needed.

But the documentation was sparse. I tried configuring hooks in various places before understanding the correct structure.

My first attempt failed:

~/.claude/settings.json (WRONG)
{
"stopHooks": {
"afterEdit": ["npm run lint"]
}
}

Nothing happened. Claude edited files, but no linter ran.

After digging through examples, I realized hooks are configured under a hooks object with specific action types. The correct structure uses PostToolUse for actions after tool execution.

Solution

The correct configuration looks like this:

~/.claude/settings.json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": ["npm run lint", "npm run format"]
}
]
}
}

The key components:

  1. PostToolUse - Hooks that run after a tool completes
  2. matcher - Regex pattern matching tool names (Edit and Write for file changes)
  3. hooks - Array of commands to execute

For plan auditing, I added another hook:

~/.claude/settings.json (extended)
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": ["npm run lint", "npm run format"]
},
{
"matcher": "TodoWrite",
"hooks": ["echo 'Plan created - check for edge cases'"]
}
]
}
}

Reason

Claude Code hooks work by intercepting tool execution at specific points:

  • PreToolUse - Before a tool runs (for validation or parameter modification)
  • PostToolUse - After a tool completes (for follow-up actions)
  • Stop - When the session ends (for final verification)

When Claude uses a tool that matches the matcher pattern, the configured hooks execute automatically. This transforms the workflow:

Before hooks:

User: "Write a function"
Claude: *writes code*
User: "Run linter"
Claude: *runs linter*
User: "Fix errors"
Claude: *fixes errors*
User: "Run linter again"
Claude: *runs linter*

After hooks:

User: "Write a function"
Claude: *writes code*
[linter runs automatically, errors caught immediately]

The cognitive load difference is significant. Instead of remembering to request follow-ups, the guardrails are just… there.

Common Hook Use Cases

Hook TypeMatcherUse Case
PostToolUseEdit|WriteLint/format after file changes
PostToolUseBashValidate command output
Stop*Final audit before session ends

Why This Matters

  1. Consistency - Hooks run every time, no exceptions
  2. Reduced Context Switching - No mental overhead for routine checks
  3. Quality Enforcement - Impossible to forget linting or testing
  4. Workflow Automation - Manual back-and-forth becomes automatic pipeline

The Reddit thread that started this journey had a great summary: “It turns a back-and-forth workflow into something that is less demanding. You set it once and the guardrails are just… there.”

Summary

Claude Code stop hooks automate repetitive follow-up tasks. Configure them once in ~/.claude/settings.json to run linters, tests, or validations automatically after Claude completes actions.

The setup requires:

  1. hooks object with action types (PostToolUse, PreToolUse, Stop)
  2. matcher regex to target specific tools
  3. hooks array with commands to execute

This transforms Claude from a smart autocomplete into a reliable collaborator with built-in quality checks.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments