HookStack
All guides

What Are Claude Code Hooks? A Practical Guide

6 min read · Reviewed 2026-06-12

Claude Code hooks are small scripts that run automatically at fixed points in an AI coding session — before a tool runs, after a file is written, when the agent stops. They turn “please remember to…” into a guarantee the model cannot skip.

This guide explains what a hook is, how the lifecycle works, why hooks beat prompt instructions for anything that must always happen, and how to install your first hook in under a minute.

What is a Claude Code hook?

A Claude Code hook is a Node.js script (a .mjs file) registered in .claude/settings.json and bound to a lifecycle event. When that event fires, Claude Code runs the script in a separate process, passes it context on stdin as JSON, and reads its stdout. The model itself never executes the hook and cannot choose to skip it.

Because hooks run outside the model, they are deterministic: the same event always triggers the same script with the same logic. That is the core property that makes them useful for guardrails, gates, and automation.

How does the hook lifecycle work?

Claude Code exposes a set of lifecycle events. The most common ones are:

  • PreToolUse — runs before a tool (Bash, Write, Edit, WebFetch…) executes. It can block the action by returning { "decision": "block", "reason": "…" } on stdout.
  • PostToolUse — runs after a tool completes. Used for auto-formatting, linting, and type-checking.
  • UserPromptSubmit — fires on each prompt. Used to inject project context, the date, or conventions.
  • Stop — fires when Claude finishes a task. Used for tests, changelogs, and quality gates.
  • SessionStart / SessionEnd — fire at session boundaries. Used for git context injection and audit logs.
  • Notification, SubagentStop, PreCompact — fire on user-input requests, subagent completion, and before context compaction.

A hook reads its input with JSON.parse(readFileSync(0, "utf8")), decides what to do, and writes a small JSON result (or stays silent). PreToolUse hooks are the only ones that can stop an action before it happens.

Why use hooks instead of prompt instructions?

Prompt instructions — including a CLAUDE.md file — are probabilistic. The model reads them and usually follows them, but it can drift, forget, or decide an exception applies. For anything that must happen every single time, “usually” is not good enough.

Hooks are deterministic Node.js scripts that execute unconditionally on matching events. There is no drift, no hallucination, and no forgotten rule. They also cost zero tokens: the script runs in its own process and never enters the model’s context window unless it explicitly returns a message.

What can you do with Claude Code hooks?

A few high-value patterns the HookStack catalogue covers:

  • Block a leaked API key before a shell command runs (PreToolUse).
  • Run your test suite when the agent says it is done (Stop).
  • Auto-format and lint every file the moment it is written (PostToolUse).
  • Inject your project conventions and the current date on every prompt (UserPromptSubmit).
  • Refuse edits on the main branch until you create a feature branch (PreToolUse).

How do I install a Claude Code hook?

With HookStack it takes three steps: browse the catalogue, select the hooks you want, and run the generated command in your project root.

npx hookstack-cli@latest install

The CLI writes the scripts to .claude/hooks/ and patches your .claude/settings.json — nothing else is touched.

Are hooks safe, and do they slow Claude down?

Hooks run in a separate process with explicit timeouts, so a slow or missing tool cannot hang your session. PostToolUse hooks are non-blocking by design: if an external tool is not installed, they simply exit quietly. Keep heavy hooks fast by filtering on file extension before running an expensive command.

Frequently asked questions

Do Claude Code hooks work on Windows?
Yes. Hooks are Node.js scripts and Node is the one runtime Claude Code guarantees, so the same `.mjs` runs on macOS, Linux, and Windows.
Do hooks consume tokens from my context window?
No. Hooks run in a separate process outside the model. They can inject context via stdout if you choose, but the script and whatever it computes never reach the model unless you return a message.
Can a hook block Claude from doing something?
Yes — a PreToolUse hook can block the action by writing { "decision": "block", "reason": "…" } to stdout before the tool runs.
Where do hooks live in my project?
Scripts live in .claude/hooks/ and are referenced by event in .claude/settings.json.

Related hooks

Sources