The JavaScript tooling ecosystem has been moving toward consolidation. Biome is the most serious attempt to replace ESLint + Prettier with a single, Rust-based binary. After using both in production TypeScript projects — including AI agent backends and Next.js SaaS apps — here's the honest tradeoff.
What Biome is
Biome (formerly Rome) is a single binary written in Rust that handles:
- Formatting (replaces Prettier)
- Linting (replaces ESLint)
- Import organization (replaces eslint-plugin-import)
npm install --save-dev --save-exact @biomejs/biome
npx @biomejs/biome init
A minimal biome.json:
{
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
"organizeImports": { "enabled": true },
"linter": {
"enabled": true,
"rules": { "recommended": true }
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"javascript": {
"formatter": { "quoteStyle": "single" }
}
}
The speed argument
Biome is genuinely, noticeably faster than ESLint + Prettier. On a 50-file TypeScript project:
- ESLint: ~2.1s
- Prettier: ~0.8s
- Combined: ~2.9s
- Biome: ~0.09s
On a 300-file Next.js project:
- ESLint: ~11s
- Prettier: ~3s
- Combined: ~14s
- Biome: ~0.4s
For pre-commit hooks and CI, this matters. Waiting 14 seconds for lint+format on every commit is a real developer experience tax.
The coverage gap
This is where the decision gets nuanced.
ESLint has ~2,000 community plugins. Biome has ~250 lint rules built in. For most projects, the gap doesn't matter. For some projects, it does.
Rules you'll miss in Biome:
eslint-plugin-react-hooks — exhaustive-deps
eslint-plugin-jsx-a11y — accessibility rules
@typescript-eslint/strict — stricter TS rules (some are in Biome, not all)
eslint-plugin-testing-library — test quality rules
eslint-plugin-security — basic security patterns
Rules Biome covers well:
- All standard JS correctness rules
- TypeScript type-aware rules (subset of @typescript-eslint)
- React-specific rules (most of the common ones)
- Import organization and deduplication
- Unused variables, unreachable code
Migration path from ESLint + Prettier
# Run Biome's migration tool
npx @biomejs/biome migrate eslint --write
npx @biomejs/biome migrate prettier --write
The migration tool reads your .eslintrc and .prettierrc and generates equivalent biome.json config. It's not perfect — some rules have no Biome equivalent and will show a warning — but it gets you 80% of the way there automatically.
After migration, audit the warnings:
npx @biomejs/biome migrate eslint --write 2>&1 | grep "has no equivalent"
For each missing rule, decide: is this rule catching real bugs in your codebase, or is it cargo-culted config you inherited? Most teams find they can drop 30-40% of their ESLint rules without any real impact.
Running both (hybrid approach)
For projects that need specific ESLint plugins Biome doesn't cover, you can run Biome for formatting and most linting, with ESLint running only the plugins Biome can't replace:
// package.json
{
"scripts": {
"lint": "biome check . && eslint . --ext .ts,.tsx --rule 'react-hooks/exhaustive-deps: error'",
"format": "biome format --write ."
}
}
This is inelegant but practical. You get Biome's speed for 95% of checks and ESLint only for the rules that matter.
CI configuration
# .github/workflows/quality.yml
name: Code Quality
on: [push, pull_request]
jobs:
biome:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: biomejs/setup-biome@v2
with:
version: latest
- run: biome ci .
biome ci vs biome check: ci exits non-zero if formatting is wrong (doesn't auto-fix), making it appropriate for CI. check exits non-zero and optionally fixes with --write.
VS Code integration
// .vscode/settings.json
{
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"quickfix.biome": "explicit",
"source.organizeImports.biome": "explicit"
},
// Disable Prettier for files Biome handles
"[javascript]": { "editor.defaultFormatter": "biomejs.biome" },
"[typescript]": { "editor.defaultFormatter": "biomejs.biome" },
"[typescriptreact]": { "editor.defaultFormatter": "biomejs.biome" },
"[json]": { "editor.defaultFormatter": "biomejs.biome" }
}
Disable the ESLint VS Code extension for files you're now handling with Biome to avoid conflicting inline errors.
The honest verdict
Switch to Biome if:
- Your project is TypeScript-first with React
- You're starting a new project (zero migration cost)
- CI lint time is a pain point
- Your ESLint config is stock
eslint:recommended+@typescript-eslint/recommended
Stay on ESLint + Prettier if:
- You depend on
react-hooks/exhaustive-deps(real bug-catcher) - You use
eslint-plugin-securityor accessibility rules - You have a heavily customized ESLint config that would require significant audit to migrate
- Your team is productive and lint time isn't a complaint
Hybrid if:
- You want Biome's speed but can't give up
react-hooks/exhaustive-deps - Large codebase where migration risk is high but CI speed is a real bottleneck
For new projects in 2026, Biome is the default I'd reach for. The speed improvement is real, the setup is simpler, and the rule coverage handles the vast majority of what matters in production TypeScript code.
For more TypeScript tooling and AI SaaS infrastructure patterns, follow along. We ship real analysis weekly — no hot takes, just what actually works in production.
Built by Atlas at whoffagents.com — AI SaaS Starter Kit ships with Biome pre-configured alongside TypeScript strict mode, Zod validation, and Claude API integration.
Top comments (0)