Claude Corner

Formatting Nits on Every Claude Diff? Let a Hook Run Prettier After Each Edit.

15 Oct 2026

Claude Corner illustration for “Formatting Nits on Every Claude Diff? Let a Hook Run Prettier After Each Edit.”

At a glance

  • A PostToolUse hook in .claude/settings.json runs Prettier on every file Claude edits or writes, so formatting never depends on Claude remembering to do it.
  • Since v2.1.90, a format hook that rewrites the file no longer makes Claude's next edit fail with "File content has changed."
  • The setup below needs v2.1.191 or later. At 5:55 AM ET on Thursday 15 October, every npm channel cleared it: stable at v2.1.292 and latest and next at v2.1.300.

Claude finishes the feature, you open the diff, and half the changed lines are quote style, trailing commas, and wrapping. Or the diff looks fine and CI fails the format check ten minutes later. You can ask Claude to run the formatter, and it usually will, but usually is the problem. Claude Code has a deterministic way to make it happen every time.

Why it matters

An instruction in CLAUDE.md or a prompt is a request the model can skip under a long context or a busy turn. A hook is not. The docs describe hooks as shell commands Claude Code runs at fixed points in its lifecycle, so certain actions always happen rather than relying on the model to choose to run them.

PostToolUse fires right after a tool call succeeds. With a matcher on Edit and Write, it runs only after file edits, not after every Read or Bash call. The hook gets the event as JSON on stdin, and for the file tools tool_input.file_path is always an absolute path (a v2.1.89 fix made that match the docs). That path is all a formatter needs.

The old catch was that the hook changes the file behind Claude's back, which could make Claude's next edit to the same file fail. The v2.1.90 changelog fixed Edit and Write failing with "File content has changed" when a PostToolUse format-on-save hook rewrites the file between consecutive edits.

How to format every Claude edit with a hook

  1. Check your build with claude --version and look for v2.1.191 or later. The comma-separated matcher below silently never fired before that release, and the stale-edit fix above arrived in v2.1.90. Make sure jq is installed and Prettier is a dev dependency of the project, so npx uses the local copy.
  2. Add the hook to .claude/settings.json in the project root, which you can commit so the whole team gets it. If the file already has a hooks key, add PostToolUse next to the existing events instead of replacing the object.
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit, Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write --ignore-unknown \"$(jq -r .tool_input.file_path)\""
          }
        ]
      }
    ]
  }
}
  1. Run /hooks and confirm the entry shows up under PostToolUse. Edits to settings files are normally picked up while the session runs; restart if the entry does not appear after a few seconds.
  2. Test it the way the docs suggest: ask Claude to add a line with single-quoted strings to a JavaScript file, then open the file. With Prettier's defaults, the quotes come back double.
  3. A successful hook shows nothing in the conversation. To see what happened, press Ctrl+O for the transcript view, or start a session with claude --debug-file /tmp/claude.log and read the log.

The command above is our variant, not the docs' one-liner, which pipes the path through xargs. We pass it as one quoted argument instead, so a path with a space stays one file, and we add Prettier's --ignore-unknown flag so a shell script or any other file Prettier has no parser for is skipped instead of raising an error. We tested this variant on one machine only, by running the command directly, not inside a live Claude Code session and not on Windows. If you would rather stay on documented ground, use the docs' xargs form from the hooks guide. In that test, on Prettier 3.9.10, a file with a syntax error made Prettier exit with code 2, and the hooks reference says exit code 2 on PostToolUse shows the hook's stderr to Claude, so Claude hears about the parse error right after the edit.

Gotchas

The matcher only covers Claude's file tools. If Claude rewrites a file through a Bash command, this hook does not run; for full coverage the docs point to a Stop hook that scans the working tree once per turn, or a FileChanged hook for specific files. A PostToolUse hook cannot undo anything, since the edit already happened. Keep the formatter fast, because it runs on every single edit. Command hooks run with your full user permissions, so review a shared .claude/settings.json before you trust it.

Recommended AI prompt

Copy this paragraph into ChatGPT, Claude, Gemini, Grok, or whatever AI you use.

Prompt

I use Claude Code and want every file it edits to be formatted automatically with a PostToolUse hook in .claude/settings.json. Ask me which languages and formatters my project uses and how my CI checks formatting, then write the hook entries I need, one per formatter, with the right flags so unsupported files are skipped. Finish with a short test I can run in a session to prove each hook fires and a list of cases the hook will miss.