Claude Code with Biome: Fast Linting and Formatting
Why Claude writes ESLint-style code even when your project uses Biome
Biome is a single Rust-based tool that replaces both ESLint and Prettier. It lints and formats JavaScript, TypeScript, JSX, TSX, JSON, and CSS in a single pass. On a 10,000-file codebase, it runs in under a second. On the same codebase, ESLint takes 45 seconds and Prettier takes 12 seconds. It ships as a single binary with no plugin ecosystem to manage.
The problem is that Claude Code's training data contains far more ESLint and Prettier configuration than Biome. When Claude generates code, it applies style rules it learned from the majority of public codebases: specific quote styles, semicolons or no semicolons, double-space indentation. These defaults may not match your Biome configuration. More importantly, Claude may generate ESLint disable comments (// eslint-disable-next-line) in code it writes, even when your project has deleted ESLint entirely.
The second issue is import ordering. Biome's import organiser (organizeImports) enforces a specific import sort order: Node.js built-ins first, then external packages, then local paths. Claude does not sort imports in the format Biome expects and often generates long blocks of unsorted imports that Biome reformats on the first check, producing a noisy diff.
The third issue is the formatter's opinionated defaults. Biome formats with 2-space indentation and double quotes by default, but these are configurable in biome.json. Claude does not read biome.json to learn your project's formatting preferences. Without a CLAUDE.md that declares your chosen settings, Claude generates code that gets reformatted on every hook run.
A CLAUDE.md that declares Biome as the single toolchain, your biome.json key settings, and the check command as the quality gate prevents all three issues.
For the testing toolchain that typically pairs with Biome in 2026 TypeScript projects, Claude Code with Vitest covers the test configuration. For the complete project setup that includes Biome, Vitest, and TypeScript, Claude Code project setup guide covers the full stack.
The Biome CLAUDE.md template
# Biome rules
## Stack
- @biomejs/biome ^1.x (linting and formatting)
- biome.json at project root
- NO eslint, NO prettier, NO eslint-disable comments
## Commands
- Check (lint + format): npx @biomejs/biome check --write .
- Lint only: npx @biomejs/biome lint .
- Format only: npx @biomejs/biome format --write .
- CI (no writes): npx @biomejs/biome ci .
## Formatting rules (from biome.json)
- Indent: spaces, 2 wide
- Quotes: double
- Semicolons: always
- Line width: 100
- Trailing commas: all
- Import organiser: enabled (organizeImports)
## Import order (enforced by Biome organiser)
Biome sorts imports automatically. Do not manually sort - let the tool handle it.
Order: Node.js built-ins → external packages → @/ aliased imports → relative imports
## Hard rules
- NEVER write eslint-disable, eslint-disable-next-line, or prettier-ignore comments
- NEVER install eslint or prettier packages
- NEVER configure .eslintrc, .prettierrc, or .eslintignore files
- ALWAYS write code that passes biome check --write before submitting
- ALWAYS use the project's biome.json settings, not Biome defaults
- When unsure about a style rule: run biome check --write and let it decide
Setting up Biome in a new project
Installing Biome takes three commands:
npm install --save-dev --save-exact @biomejs/biome
npx @biomejs/biome init
npx @biomejs/biome check --write .
The init command creates a biome.json with sensible defaults. The --save-exact flag pins the exact version, which matters because Biome's formatter may produce different output across minor versions. Pinning prevents unexpected formatting diffs when teammates run npm install.
A production-ready biome.json for a TypeScript/React project:
{
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"noUnusedVariables": "error",
"noUnusedImports": "error"
},
"suspicious": {
"noExplicitAny": "warn"
},
"style": {
"useConst": "error",
"noVar": "error"
}
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"semicolons": "always",
"trailingCommas": "all"
}
},
"files": {
"ignore": ["node_modules", ".next", "dist", "build", "coverage"]
}
}
The noUnusedVariables and noUnusedImports rules at the error level are important: Claude Code often generates helper variables and imports that are used during the generation session but left in the code after the feature is complete. These rules surface them immediately.
Connecting Biome to Claude Code hooks
Claude Code hooks let you run shell commands automatically during the agent's workflow. A PostToolUse hook that runs biome check --write after every file write means every file Claude touches is immediately formatted and linted.
In your project's .claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "npx @biomejs/biome check --write ${file}"
}
]
}
]
}
}
With this hook, Claude's writes are immediately reformatted by Biome. Claude sees the reformatted result in subsequent reads, which teaches it the actual output format and makes its future writes more accurate. The feedback loop is tighter than relying on Claude to apply the formatting rules from memory.
For a global hook that applies across all projects, add the same configuration to ~/.claude/settings.json.
The Claude Code hooks guide covers the full hook system, including PreToolUse hooks that can block Claude from making changes that would break the linter before they are written.
Migrating from ESLint and Prettier
Biome can import your existing ESLint configuration and translate rules it supports:
npx @biomejs/biome migrate eslint --write
This reads .eslintrc.* and eslint.config.* and creates equivalent Biome rules in biome.json. Not all ESLint rules have Biome equivalents, particularly plugin-specific rules (React hooks, import/no-cycle, etc.). The migration command outputs a list of rules it could not translate.
For the untranslated rules, you have three options:
Accept that Biome does not enforce them. Biome's built-in rules cover the most impactful correctness and style issues. Plugin-specific rules are often lower value.
Use
biome.json'signorelist to silence specific patterns until you can audit them manually.Keep a minimal ESLint config for the specific rules Biome cannot handle. This is not ideal, but it is better than blocking the migration.
The Prettier migration is simpler because Biome and Prettier format the same code nearly identically when configured with matching settings. The key differences: Biome always adds trailing commas in function parameters (Prettier made this optional in v3), and Biome does not support .prettierignore (use biome.json files.ignore instead).
This is where Claudify saves time: your CLAUDE.md comes pre-populated with your Biome configuration extracted from your biome.json, so Claude Code generates compliant code from day one and the Biome hook keeps every file clean automatically.
Type-aware linting with Biome
Biome v2 introduced type-aware lint rules that use the TypeScript type system to catch additional errors. These rules are more powerful but slower because they require TypeScript type resolution.
Enable type-aware linting in biome.json:
{
"linter": {
"rules": {
"nursery": {
"noFloatingPromises": "error",
"noMisusedPromises": "error"
}
}
}
}
The noFloatingPromises rule catches async function calls that are not awaited and not explicitly discarded with void. This is one of the most common bugs in Claude-generated code: Claude generates fetchUserData() without await when it should be await fetchUserData(), and the function exits before the async operation completes.
The noMisusedPromises rule catches Promises passed to non-async callback arguments. This catches the common mistake of passing an async function to Array.forEach (which ignores the returned Promise) instead of using for...of or Promise.all.
Type-aware rules require the TypeScript language server to be running, which adds startup time to biome check. For pre-commit hooks where you want instant feedback, run without type-aware rules. For CI where you want full correctness checking, enable them.
Biome in CI
The biome ci command runs lint and format checks without writing any changes. It exits with a non-zero code if any check fails, which blocks the CI pipeline.
# .github/workflows/ci.yml
- name: Biome check
run: npx @biomejs/biome ci .
Biome processes files in parallel across all CPU cores. On a standard CI runner (4 cores), a 500-file TypeScript project runs in under 500ms. The entire linting step takes less time than most CI checkout actions.
For monorepos with multiple packages, run Biome from the root with a single biome.json that applies to all packages:
npx @biomejs/biome ci --config-path=./biome.json ./packages
Each package can extend or override the root config by placing its own biome.json in the package directory.
FAQ
Does Biome replace TypeScript's tsc type checking?
No. Biome lints and formats code but does not perform full TypeScript type checking. Run tsc --noEmit separately in CI for type safety. The two tools are complementary: Biome catches style and common correctness issues fast, TypeScript catches type errors.
Can Biome handle .vue or .svelte files?
Biome handles .js, .ts, .jsx, .tsx, .json, .jsonc, and .css files. It does not handle .vue, .svelte, or .astro files as of 2026. For Vue and Svelte projects, ESLint with the relevant framework plugin remains necessary for template linting. Biome handles the TypeScript files within those projects.
How does Biome handle files that ESLint is currently ignoring?
Biome uses files.ignore in biome.json instead of .eslintignore. After migration, check that all intentionally-ignored paths (generated files, build output, vendor code) are in biome.json's files.ignore array. Files not in the ignore list will be linted and formatted, which may produce unexpected changes on generated files.
Get Claudify. Your CLAUDE.md ships configured for Biome with your project's formatting rules pre-declared so Claude Code writes compliant code from the first line and the hook keeps every file clean automatically.
More like this
Ready to upgrade your Claude Code setup?
Get Claudify