Formatter Hooks
PostToolUse hooks that keep Claude Code's output formatted correctly — automatically, silently, and using the same config your team already committed. The single highest install-to-value category in the hooks library.
Why formatter hooks are the highest install-to-value ratio in the whole hooks library
Formatter hooks are the "install once, never think about it again" category. You add three lines to settings.json, and from that moment on every file Claude Code writes or edits comes out formatted correctly — without you asking, without slowing the interaction down, without leaving any drift for humans to clean up later. Total cognitive overhead after installation: zero.
The 7 hooks on this page cover the mainstream formatters for JavaScript/TypeScript (Prettier, ESLint, tsc), Python (Black, Ruff), Go (gofmt), and Rust (rustfmt). Each one is a small bash script wrapped around the existing CLI, so they're transparent, easy to audit, and use the same config your team already committed.
PostToolUse hook matching Write|Edit. When Claude writes a file with the matching extension, the hook runs the formatter. If the formatter succeeded, the operation continues; if it found errors it can't fix, the output goes back to Claude so it can address them on the same turn. That's the whole loop.
Anatomy of auto-prettier
Twenty-five lines of bash, and Prettier now runs on every JavaScript, TypeScript, CSS, HTML, JSON, Markdown, or YAML file Claude touches. Notice the exit 1 on failure: that's warn, not block. Formatter failure shouldn't stop the whole operation — better to keep going and let Claude notice the formatting complaint on the next turn.
The exit-code discipline that makes formatters comfortable to install
This is the design decision people get wrong on their first formatter hook and regret immediately:
- Formatter success →
exit 0. Silent, no output. This should be 99% of runs. - Formatter can't fix the file (syntax error, config problem) →
exit 1. Non-blocking warn. Claude sees the error and usually fixes it on the same turn. - Formatter isn't installed or misconfigured →
exit 0with a stderr log. Don't punish Claude for a missing dev dependency.
The instinct is to exit 2 (block) on any formatter error — don't. A blocking formatter turns every syntax mistake into a wall Claude can't get past on the current message. Warning lets Claude see the issue and fix it next turn.
Linters and type-checkers in the same category
Three of the seven hooks aren't strictly "formatters" — they're static checks that also live in PostToolUse:
auto-eslint— runseslint --fix. Auto-fixes what it can, warns about what it can't.auto-ruff— runsruff check --fixandruff format. Ruff is fast enough that you can run both on every write.auto-typecheck— runstsc --noEmiton the affected package. Not fast, but essential for catching type errors before they compound.
Same exit-code discipline applies. Warn, don't block. The exception is auto-typecheck on some teams — if you care deeply about type safety, blocking on type errors is defensible. Most teams find warning gives Claude enough signal without slowing interactive work.
Performance — the one gotcha
Every PostToolUse hook runs on every matching write. For a session with 30 file edits, that's 30 formatter runs. Rules to keep it comfortable:
- Format single files, not the project.
prettier --write $file_path, notprettier --write .. - Skip type-check on unrelated packages in a monorepo.
auto-typecheckshould read the file path and only check the containing package. - Prefer Ruff over Black + Flake8 if you're on Python. Ruff is ~100x faster and handles both jobs.
- Skip generated files. Add early exits for paths matching
dist/,node_modules/,.next/,__generated__/. Formatting generated files wastes cycles.
All 7 formatter hooks, grouped by language
Every hook is a small bash wrapper around your existing formatter CLI. Transparent, easy to audit, uses your project's committed config.
Which formatter hooks should I install?
Start with your primary language; add hooks per additional language as needed. All coexist cleanly.
tsc across the whole tree on every edit. Read the file path in the hook and scope accordingly.auto-prettier, auto-ruff, and auto-gofmt all run without stepping on each other.PostToolUse chain exceeds ~2 seconds per write, Claude interactions start to feel sluggish. Prefer Ruff over Black, skip tsc in fast-loop sessions, exclude generated files.Related sub-categories in Hooks
Formatter hooks pair naturally with approval gates, security enforcement, test hooks, and logging — the four categories most mature Claude Code setups install together.
🐛 Hit an error while using these?
Our sister site AI Error Hub covers Claude Code errors, MCP connection failures, and stack traces — cross-referenced with everything on this site.
Frequently asked questions
The questions developers ask most about formatter.
The one for your primary language:
- TypeScript →
auto-prettier+auto-eslint - Python →
auto-ruff - Go →
auto-gofmt - Rust →
auto-rustfmt
Formatter hooks have the highest install-to-value ratio in the whole hooks library: three lines of settings.json and Claude's output is formatted correctly forever.
Warn (exit 1), not block (exit 2).
Blocking on formatter failure means every syntax error becomes a wall Claude can't get past on the current turn. Warning lets Claude see the error message and fix it on the next turn — the actual desired behavior.
Reserve block-level severity for security hooks and destructive-operation gates.
A little, but usually not enough to notice — most formatters are sub-second:
- Prettier on a single file — ~100ms
- Ruff — ~30ms
- gofmt — under 10ms
The exception is auto-typecheck, which can take a second or two depending on project size.
If you're editing many files in tight loops: skip type-check, use Ruff instead of Black+Flake8, exclude generated files, and scope typecheck to the affected package only.
auto-ruff, in almost every case:
- ~100x faster than Black
- Handles both formatting and linting
- Formatter output matches Black's style by default
The only reason to prefer auto-black-python is if your team already committed a Black-specific config and doesn't want to switch. For new projects, use Ruff.
The hook should exit 0 (success) with a stderr log noting the missing dependency — not fail loudly and interrupt the workflow.
That way Claude Code sessions keep working for teammates who haven't installed the dev deps yet, while still leaving a paper trail so someone eventually notices and fixes it.
Don't punish the interaction for a missing dev dependency.
Yes — because they invoke the formatter's own CLI, which reads those ignore files natively. No extra config needed.
If your team already committed .prettierignore excluding generated dirs, the hook automatically respects that. Same for .eslintignore, Ruff's [tool.ruff] excludes, and rustfmt.toml's ignore patterns.
Yes, and it's common. auto-prettier + auto-eslint + auto-typecheck all run on TypeScript writes.
They chain in the order they're declared in settings.json. Just watch total latency — three formatters running on every write can add up.
In monorepos, keep typecheck scoped to the affected package to avoid the slowest hook running project-wide.
They're complementary layers:
- Formatter hooks catch things as Claude writes them — immediate feedback, no drift accumulation.
- Git pre-commit hooks catch things that reach the commit stage regardless of how they got there (Claude, another dev, an editor plugin).
Best practice: both. Formatter hooks for interactive Claude Code work, pre-commit for the git boundary.
No. Hooks fire on tool-use events regardless of who invoked the tool. When a subagent writes a file, the PostToolUse formatter hooks run just as they would in the main conversation.
This is important — team-shared formatters must apply uniformly. If security-reviewer or postgres-dba writes a migration file, it still gets formatted.
Yes, absolutely. Commit .claude/settings.json and .claude/hooks/ to git.
Every teammate gets the same formatters on next pull, with zero manual setup. This is exactly the pattern that makes Claude Code output indistinguishable from human output — no drift, no "wait, my Prettier is on a different version", no cleanup PRs.
Commit and forget.
Get the weekly Claude Code digest
Every Tuesday: new formatter, Anthropic release recap, and the best community submission. 13,000+ developers read it.