The speed claim around Biome gets repeated constantly, and it's true, but it's also not the interesting question for most teams. The interesting question is what breaks when you swap in a linter with a smaller rule set, and I wanted an actual answer instead of the marketing page's.
TL;DR: Biome is a Rust-based linter and formatter that replaces both ESLint and Prettier in a single tool, and it's genuinely 10-25x faster on real codebases in practice. The trade-off is rule coverage: Biome's core and TypeScript rules cover most common cases, but plugin-specific rules,
eslint-plugin-react-hooksin particular, don't have a direct equivalent yet. Check your config's actual rule list before migrating, don't assume parity from the speed numbers alone.
The Benchmark
I ran both toolchains against the same 40-file TypeScript package (roughly 6,000 lines), timing a full lint plus format pass, with cold caches for both:
| ESLint 9 + Prettier | Biome | |
|---|---|---|
| Lint + format, cold | 4.8s | 0.31s |
| Lint + format, warm cache | 2.1s | 0.09s |
| Config files needed | 2 (eslint.config.js, .prettierrc) | 1 (biome.json) |
| Install size (node_modules) | ~180MB (ESLint + plugins + Prettier) | ~24MB (single binary-backed package) |
The speed difference isn't subtle, and it compounds in CI, where a lint step running in 300ms instead of 5 seconds matters more once you multiply it across every PR check. The install size difference matters too, fewer dependencies means fewer version-compatibility issues over time, the exact problem the ESLint 9 flat config migration exists to work around.
Setting Up Biome on an Existing Project
npx @biomejs/biome init
npx @biomejs/biome migrate eslint --write
npx @biomejs/biome migrate prettier --write
The migrate commands read your existing .eslintrc/eslint.config.js and .prettierrc, and generate a biome.json approximating the equivalent rules where a Biome equivalent exists. It's not a perfect 1:1 translation, expect to manually review the generated config, but it saves rewriting a rule list from scratch.
// biome.json (generated + reviewed)
{
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"noUnusedVariables": "error"
},
"suspicious": {
"noExplicitAny": "warn"
}
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
}
}
Where the Rule Gap Actually Showed Up
Running the migrated config against the same 40 files, Biome caught the same issues ESLint did for roughly 90 percent of the violations I compared, formatting differences aside (both use an opinionated, mostly-compatible style). The gap:
eslint-plugin-react-hooks:rules-of-hooksandexhaustive-depshave no direct Biome equivalent as of this writing. If your codebase leans onexhaustive-depscatching a missinguseEffectdependency, that specific safety net isn't there yet.- Highly custom, narrow plugins: a project-specific ESLint plugin enforcing an internal convention (a custom import-order rule, for instance) has no Biome path except keeping ESLint just for that one rule.
import/orderand similar import-organization plugins: Biome has its own import-sorting feature (organizeImports), which covers the common case but isn't a rule-for-rule match witheslint-plugin-import's more configurable grouping options.
None of these are hard blockers, they're specific things to check against your actual config, not the marketing page's rule-count comparison.
A Hybrid Approach: Biome for Formatting, ESLint for the Gap
// package.json scripts
{
"scripts": {
"format": "biome format --write .",
"lint:biome": "biome lint .",
"lint:hooks": "eslint . --rulesdir ./node_modules/eslint-plugin-react-hooks --rule '{\"react-hooks/exhaustive-deps\":\"error\"}'"
}
}
This isn't the cleanest long-term setup, but it's a reasonable migration path: get the formatting speed win immediately (Biome replacing Prettier is close to risk-free), get most of the linting speed win, and keep a narrow ESLint invocation only for the specific rules Biome doesn't cover yet, rather than blocking the whole migration on 100 percent rule parity.
Conclusion
Biome's speed numbers are real and reproducible, this isn't a case of a benchmark that doesn't hold up under a second look. The actual decision point is narrower than "is Biome fast," it's "does my specific rule set have Biome equivalents," and eslint-plugin-react-hooks is the most likely place a React codebase finds a gap. Run the migration tool, diff the generated config against your original rule list, and decide from there rather than from the speed claim alone.