Biome Check Hook for Claude Code
Read-only Claude Code PostToolUse hook that runs Biome's formatter, linter, and import-sorting checks on edited JavaScript, TypeScript, JSON, CSS, and GraphQL files without auto-writing changes.
Open the source and read safety notes before installing.
Safety notes
- This hook runs `biome check` without `--write`, so it reports formatter, linter, and import-sorting diagnostics but does not modify files.
- Do not add `--write` or `--unsafe` until the team has reviewed the hook behavior, backup expectations, and failure mode for generated edits.
- The hook exits non-zero when Biome reports diagnostics. Depending on Claude Code settings, this can interrupt the current workflow until issues are reviewed.
- Use a project-pinned Biome package when possible. The script prefers `./node_modules/.bin/biome` and avoids network-install fallbacks.
- Keep matchers scoped to write/edit tools. Running Biome after every tool call can add noise and slow down large projects.
Privacy notes
- Biome diagnostics can include file paths, rule names, code excerpts, import names, comments, and source snippets from edited files.
- Claude Code hook logs, terminal scrollback, screenshots, support tickets, and AI transcripts can retain Biome diagnostics outside the repository.
- Avoid pasting real customer code, private filenames, generated secrets, or proprietary source excerpts from hook output into public issue comments.
Prerequisites
- Claude Code project where hooks are allowed by user or project policy.
- Project-local Biome install, usually `pnpm add -D @biomejs/biome` or the equivalent package-manager command.
- `jq` available on the machine to parse Claude Code hook input.
- A reviewed `.claude/settings.json` or user settings hook configuration for `Write`, `Edit`, and `MultiEdit`.
Schema details
- Install type
- cli
- Reading time
- 7 min
- Difficulty score
- 63
- Troubleshooting
- Yes
- Breaking changes
- No
- Scope
- Source repo
- Trigger
- PostToolUse
- Script language
- bash
Script body
#!/usr/bin/env bash
set -uo pipefail
input="$(cat)"
if ! command -v jq >/dev/null 2>&1; then
echo "Biome hook skipped: jq is required to parse Claude Code hook input." >&2
exit 0
fi
tool_name="$(printf '%s' "$input" | jq -r '.tool_name // .toolName // empty')"
file_path="$(printf '%s' "$input" | jq -r '.tool_input.file_path // .tool_input.path // .toolInput.file_path // .toolInput.path // empty')"
case "$tool_name" in
Write|Edit|MultiEdit|write|edit|multiedit) ;;
*) exit 0 ;;
esac
if [ -z "$file_path" ] || [ ! -f "$file_path" ]; then
exit 0
fi
case "$file_path" in
*.js|*.jsx|*.ts|*.tsx|*.mjs|*.mts|*.cjs|*.cts|*.json|*.jsonc|*.css|*.graphql|*.gql) ;;
*) exit 0 ;;
esac
if [ -x ./node_modules/.bin/biome ]; then
biome_cmd=(./node_modules/.bin/biome)
elif command -v biome >/dev/null 2>&1; then
biome_cmd=(biome)
elif command -v pnpm >/dev/null 2>&1 && pnpm exec biome --version >/dev/null 2>&1; then
biome_cmd=(pnpm exec biome)
elif command -v npm >/dev/null 2>&1 && npx --no-install biome --version >/dev/null 2>&1; then
biome_cmd=(npx --no-install biome)
else
echo "Biome hook skipped: install @biomejs/biome in this project to enable read-only checks." >&2
exit 0
fi
echo "Biome hook: checking $file_path" >&2
"${biome_cmd[@]}" check --no-errors-on-unmatched "$file_path"
status=$?
if [ "$status" -ne 0 ]; then
echo "Biome hook: diagnostics found. Review output before asking Claude to continue." >&2
fi
exit "$status"Full copyable content
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "./.claude/hooks/biome-check.sh"
}
]
}
]
}
}About this resource
Overview
This hook runs Biome's check command after Claude Code writes or edits a file
that Biome can handle. It is intentionally read-only: it reports diagnostics and
exits non-zero when Biome finds issues, but it does not pass --write,
--unsafe, start the Biome daemon, or create formatter changes automatically.
Use it when a project already uses Biome and you want immediate feedback after Claude changes JavaScript, TypeScript, JSON, CSS, or GraphQL files.
Requirements
- Claude Code hooks enabled in user or project settings.
- Biome installed in the project, preferably as a pinned dev dependency.
jqinstalled locally so the script can read Claude Code hook input.- A reviewed hook configuration scoped to
Write,Edit, andMultiEdit.
Hook Configuration
Add the hook command to .claude/settings.json or the appropriate user-level
Claude Code settings file:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "./.claude/hooks/biome-check.sh"
}
]
}
]
}
}
Hook Script
Save this as .claude/hooks/biome-check.sh and make it executable:
#!/usr/bin/env bash
set -uo pipefail
input="$(cat)"
if ! command -v jq >/dev/null 2>&1; then
echo "Biome hook skipped: jq is required to parse Claude Code hook input." >&2
exit 0
fi
tool_name="$(printf '%s' "$input" | jq -r '.tool_name // .toolName // empty')"
file_path="$(printf '%s' "$input" | jq -r '.tool_input.file_path // .tool_input.path // .toolInput.file_path // .toolInput.path // empty')"
case "$tool_name" in
Write|Edit|MultiEdit|write|edit|multiedit) ;;
*) exit 0 ;;
esac
if [ -z "$file_path" ] || [ ! -f "$file_path" ]; then
exit 0
fi
case "$file_path" in
*.js|*.jsx|*.ts|*.tsx|*.mjs|*.mts|*.cjs|*.cts|*.json|*.jsonc|*.css|*.graphql|*.gql) ;;
*) exit 0 ;;
esac
if [ -x ./node_modules/.bin/biome ]; then
biome_cmd=(./node_modules/.bin/biome)
elif command -v biome >/dev/null 2>&1; then
biome_cmd=(biome)
elif command -v pnpm >/dev/null 2>&1 && pnpm exec biome --version >/dev/null 2>&1; then
biome_cmd=(pnpm exec biome)
elif command -v npm >/dev/null 2>&1 && npx --no-install biome --version >/dev/null 2>&1; then
biome_cmd=(npx --no-install biome)
else
echo "Biome hook skipped: install @biomejs/biome in this project to enable read-only checks." >&2
exit 0
fi
echo "Biome hook: checking $file_path" >&2
"${biome_cmd[@]}" check --no-errors-on-unmatched "$file_path"
status=$?
if [ "$status" -ne 0 ]; then
echo "Biome hook: diagnostics found. Review output before asking Claude to continue." >&2
fi
exit "$status"
How It Works
- Reads Claude Code hook input from
stdin. - Ignores non-edit tools.
- Ignores unsupported file extensions.
- Prefers a project-local
./node_modules/.bin/biomeexecutable. - Falls back to an existing global
biome,pnpm exec biome, ornpx --no-install biomewithout installing packages from the network. - Runs
biome check --no-errors-on-unmatched <file>. - Exits with Biome's status so the user sees diagnostics before continuing.
Safety Notes
- This is a diagnostic hook, not an auto-fixer.
- Keep it read-only until a team explicitly accepts automated formatting after every AI edit.
- Avoid broad matchers such as all tool calls or all file paths.
- Treat non-zero exits as a review stop, not proof that Claude should blindly rewrite the file.
- Keep the project-local Biome version pinned so local and CI results match.
Privacy Notes
Biome diagnostics may include source excerpts, file paths, import specifiers, rule names, and snippets of edited code. Treat hook output as potentially sensitive and avoid copying it into public comments when it contains proprietary source or customer data.
Duplicate And Source Review
Current HeyClaude content already has a Biome rules entry and generic hook
generator examples that mention biome check --write. This entry is different:
it is a dedicated Claude Code hook, defaults to read-only diagnostics, and is
source-backed by Biome's CLI docs and official repository.
Troubleshooting
Issue: The hook never runs
Fix: Check the Claude Code hook matcher, script path, executable bit, and whether the edited file extension is included in the script allowlist.
Issue: The hook says Biome is not installed
Fix: Install Biome as a project dev dependency, for example
pnpm add -D @biomejs/biome, then confirm ./node_modules/.bin/biome version
works from the project root.
Issue: The hook blocks too often
Fix: Keep the hook read-only but narrow the matcher or extension list. Consider running it only for files that already live under Biome-managed paths.
Issue: The hook output exposes private code
Fix: Keep diagnostics local, redact snippets before sharing, and avoid posting hook output to public PRs, issues, chats, or screenshots.
Issue: Claude keeps trying to auto-fix every diagnostic
Fix: Tell Claude to summarize Biome diagnostics first, group them by rule,
and ask before applying changes. Do not add --write unless that behavior is
explicitly approved.
Source citations
Signals
Loading live community signals…
A short, calm digest of reviewed Claude resources. Unsubscribe any time.