Replacing ESLint + Prettier with Biome Speeds Up Linting 56x — Migration and Hybrid Strategy Guide
Honestly, I sighed the first time I saw eslint.config.js (flat config). Upgrading to ESLint v9 meant completely overhauling the existing .eslintrc.json, and since ESLint v9 changed the configuration format itself (declaring all plugins and rules in a single eslint.config.js), I had to check whether each plugin supported the new format one by one — it was hard to tell if this was a linter upgrade or a migration project. Our team had a monorepo with around 3,000 TypeScript files, and that's exactly when Biome caught my eye.
Biome is a single-binary toolchain written in Rust. The @biomejs/biome package replaces everything ESLint, Prettier, and import-sorting plugins used to do. This article covers why Biome is fast, how to add it to an existing project, and how to tell when a full migration makes sense versus when a hybrid approach is the right call — drawing from real experience. If your builds or CI are already slow because of ESLint configuration, this article alone is enough to point you in the right direction.
Core Concepts
If you already know how Biome works, feel free to skip ahead to the 'Practical Application' section.
Why Biome Is Fast — Rust Parser and Single Pass
ESLint and Prettier both run on Node.js and parse files independently. A file gets read once by ESLint, then again by Prettier. On top of that, each additional plugin adds another analysis pass.
Biome is different. Its Rust-implemented parser reads a file once to build an AST (Abstract Syntax Tree), then handles formatting and linting simultaneously on that AST. Because Rust manages memory at compile time through its ownership system, there's no GC overhead — and along with it, no Node.js runtime overhead.
The results are quite dramatic.
| Task | ESLint / Prettier | Biome | Multiplier |
|---|---|---|---|
| Linting 10,000 files | 45.2s | 0.8s | ~56x |
| Formatting 10,000 files | 12.1s | 0.3s | ~40x |
| Energy consumption (M3 Mac) | baseline | 30% of baseline | ~70% reduction |
Based on official benchmarks: 10,000 TypeScript files on an M3 MacBook Pro. The "35x faster" figure cited on DEV Community was measured under different file counts and rule configurations — the multiplier varies by conditions.
AST (Abstract Syntax Tree): An intermediate representation that converts source code into a tree structure. Linters and formatters traverse this tree to apply rules. Biome builds this tree once and reuses it.
Biome v2 "Biotype" — Type Awareness Without the Type Checker
I'll be honest — when this feature was announced, I was fifty-fifty. My first instinct was skepticism: "Can it really understand types properly without tsc?"
The biggest change in Biome v2, released in June 2025, is type-aware linting implemented without the TypeScript compiler (tsc). Previously, using type-based rules like noFloatingPromises required typescript-eslint to call tsc directly to retrieve type information — a major culprit behind slow linting. Biome v2 replaces that process with its own type inference engine, Biotype.
Per the Biome v2 official release notes, it currently detects around 75% of cases compared to typescript-eslint, but the very fact that type-based checks are now possible without any speed penalty signals a significant shift in direction.
Supported File Formats
- Full support: JavaScript, TypeScript, JSX/TSX, JSON/JSONC
- Formatter + basic linter: CSS
- Formatter only: GraphQL
- In development: HTML (beta), Vue, Svelte, Astro
If you're working on a Vue or Svelte project, it may be worth waiting a bit longer.
Practical Application
Example 1: New Project — Starting with Biome from Scratch
For a new project, starting with Biome from the beginning is recommended. There's no existing configuration, so there's no migration burden.
# 1. Install (pinning the version is recommended)
pnpm add -D --save-exact @biomejs/biome
# 2. Generate the base configuration file
pnpm biome initRunning biome init creates a biome.json. Below is a base configuration commonly used in production.
{
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"trailingCommas": "all",
"semicolons": "always"
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"typescript": {
"noExplicitAny": "warn"
}
}
},
"organizeImports": {
"enabled": true
}
}Updating the package.json scripts keeps CI cleanly connected as well.
{
"scripts": {
"lint": "biome lint .",
"format": "biome format --write .",
"check": "biome check .",
"ci": "biome ci ."
}
}| Command | Purpose |
|---|---|
biome check . |
Lint + format check only (for verifying failures in CI) |
biome check --write . |
Auto-fix lint issues + apply formatting |
biome ci . |
CI-only mode, returns results via exit code without making changes |
biome lint . |
Lint only |
biome format --write . |
Format only |
If you're using VS Code, disable the existing ESLint and Prettier extensions, install the official Biome extension, and add the following to .vscode/settings.json to enable auto-format on save.
{
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"[javascript]": { "editor.defaultFormatter": "biomejs.biome" },
"[typescript]": { "editor.defaultFormatter": "biomejs.biome" },
"[typescriptreact]": { "editor.defaultFormatter": "biomejs.biome" },
"[json]": { "editor.defaultFormatter": "biomejs.biome" },
"[jsonc]": { "editor.defaultFormatter": "biomejs.biome" }
}Including
[json]and[jsonc]prevents conflicts with Prettier when editingbiome.jsonitself in VS Code.
Example 2: Existing Project — Using Automatic Migration
For projects that already have .eslintrc.* and .prettierrc, Biome provides an automatic conversion command. I was skeptical the first time I saw it, but it works surprisingly well.
# 1. Install Biome
pnpm add -D --save-exact @biomejs/biome
pnpm biome init
# 2. Auto-convert ESLint configuration
pnpm biome migrate eslint --write
# 3. Auto-convert Prettier configuration
pnpm biome migrate prettier --write
# 4. Apply Biome to all files (review changes)
pnpm biome check --write .
# 5. Remove old packages (adjust based on your actual package.json)
pnpm remove eslint prettier eslint-config-prettier \
@typescript-eslint/eslint-plugin @typescript-eslint/parser \
eslint-plugin-react eslint-plugin-react-hooksThe removal list in step 5 is an example. In practice, adjust it based on the ESLint-related packages actually installed in your
package.json.
Rules that the automatic conversion cannot map are left as comments in biome.json. Review these manually and either replace them with Biome equivalents or remove them.
Example 3: Hybrid — Biome + ESLint Coexisting
If your project depends on plugins that Biome doesn't yet support — such as eslint-plugin-react-hooks or eslint-plugin-next — a full replacement is difficult. I referenced this approach myself: the hybrid strategy adopted by Lemonbase, a Korean HR tech startup, is a practical solution for this case.
In ESLint v9's flat config environment, the --ext flag has been removed. Extension filtering is handled inside eslint.config.js, so scripts can be written simply as follows.
{
"scripts": {
"lint:biome": "biome lint .",
"lint:eslint": "eslint .",
"lint": "pnpm lint:biome && pnpm lint:eslint",
"format": "biome format --write .",
"check": "biome check ."
}
}The key is clearly separating what Biome owns from what ESLint owns.
| Role | Responsible Tool |
|---|---|
| All formatting | Biome |
| Import sorting | Biome (organizeImports) |
| General JS/TS linting (455+ rules) | Biome |
react-hooks rules |
ESLint + eslint-plugin-react-hooks |
| Next.js-specific rules | ESLint + eslint-plugin-next |
With this setup, Prettier is removed entirely, and ESLint remains only where necessary with a significantly reduced role. The effect is cutting dependencies and configuration complexity by more than half.
Pros and Cons
Advantages
| Item | Details |
|---|---|
| Processing speed | Per official benchmarks: 56x faster than ESLint for linting 10,000 files, 40x faster than Prettier for formatting |
| Unified configuration | Dozens of packages and multiple config files consolidated into a single biome.json |
| Automated migration | biome migrate eslint/prettier --write handles most of the conversion automatically |
| Prettier compatibility | 97% compatible with Prettier output — almost no formatting conflicts |
| Built-in import sorting | organizeImports option works without any additional plugins |
| Energy efficiency | Official benchmarks show ~70% energy reduction vs. ESLint on M3 Mac |
| Single dependency | Version conflicts and compatibility issues between plugins are structurally eliminated |
Disadvantages and Caveats
| Item | Details | Mitigation |
|---|---|---|
| Plugin gaps | No support for some react-hooks, next, and import rules |
→ See Example 3 hybrid configuration |
| Type detection coverage | ~75–85% compared to typescript-eslint |
Consider running both for strict type safety requirements |
| Plugin system | First iteration in v2; not yet at ESLint ecosystem level | Gradually migrate custom rules with GritQL |
| Advanced CSS rules | Some advanced Stylelint CSS rules not supported | Can run Stylelint alongside for CSS linting |
| HTML/Vue/Svelte | Formatter not yet supported (HTML is in beta) | Consider migrating after roadmap completion for these frameworks |
GritQL: A DSL (domain-specific language) introduced in Biome v2 for writing custom lint rules. It queries code structure using SQL-like pattern matching syntax. The barrier to entry is lower than writing custom ESLint plugins, but expressiveness is still limited.
The Most Common Mistakes in Practice
- Adding Biome without disabling the Prettier and ESLint extensions in VS Code — Two formatters conflict on save, causing the code to "bounce." The order matters: disable the existing extensions at the workspace level first, then activate the Biome extension.
- Installing without
--save-exact— Biome can change formatting output even in minor updates, so the entire team must use the same version. Usepnpm add -D --save-exact @biomejs/biometo pin the version. - Confusing
biome checkandbiome ci— Locally, usebiome check --write .to apply automatic fixes; in CI, usebiome ci .to check without making changes. Including--writein CI can result in the pipeline silently modifying files and passing anyway.
Closing Thoughts
If you need to decide right now whether to switch, the key is simple: if there are no unsupported plugins, do a full migration; if there are, start with a hybrid approach. Either way, Prettier can be removed immediately, and ESLint dependencies will be reduced by more than half.
Three steps you can take right now:
- Audit your dependencies: Check ESLint-related packages in
package.json. If there are no Biome-unsupported plugins likeeslint-plugin-react-hooksoreslint-plugin-next, go for a full migration; if there are, proceed with a hybrid strategy. - Apply to a side project or branch first: You can run the automatic conversion with the four lines below. Running
biome check .will immediately show which rules are missing.bashpnpm add -D --save-exact @biomejs/biome pnpm biome init pnpm biome migrate eslint --write pnpm biome migrate prettier --write - Connect CI and your editor: Add
"ci": "biome ci ."to yourpackage.jsonscripts, and if you're on VS Code, install the Biome extension and set"editor.defaultFormatter": "biomejs.biome"in.vscode/settings.json— your entire development cycle will be wired up to Biome.
References
- Biome Official Website
- Biome GitHub Repository
- Biome Official Migration Guide (ESLint & Prettier)
- Biome Official Configuration Reference
- Biome v2 "Biotype" Official Release Notes
- Biome: The ESLint and Prettier Killer? Complete Migration Guide for 2026 | DEV Community
- Replace ESLint + Prettier with Biome: 35x Faster, One Tool | DEV Community
- Lemonbase Tech Blog: ESLint V9 Migration & Biome Hybrid Adoption
- Biome v2 Socket Analysis: Plugin System & Type-Aware Linting
- Better Stack: Biome vs ESLint In-Depth Comparison
- Migrate a Node.js project from ESLint and Prettier to Biome