Biome vs ESLint — The New JavaScript Linter War
How Biome compares to ESLint + Prettier — speed benchmarks, rule coverage, migration path, and whether it's ready to replace your current linting setup.
The standard JavaScript linting setup has been ESLint + Prettier for years. ESLint catches bugs and enforces code patterns. Prettier formats code. They work together through eslint-config-prettier (to disable ESLint formatting rules) and eslint-plugin-prettier (to run Prettier as an ESLint rule). This stack works, but it's slow, configuration-heavy, and requires maintaining compatibility between two separate tools.
Biome is the successor to Rome (the abandoned JavaScript toolchain by the creator of Babel). It's a single tool written in Rust that handles both linting and formatting. One config file. One install. One command. And it's 20-100x faster than ESLint + Prettier.
Speed
The performance difference isn't subtle.
# Benchmarking on a large TypeScript monorepo (~3000 files)
# ESLint + Prettier
time npx eslint . && npx prettier --check .
# real: 45.2s
# Biome
time npx @biomejs/biome check .
# real: 0.8s
That's not a typo. Biome processes the entire codebase in under a second where ESLint takes nearly a minute. The difference comes from:
- Rust vs JavaScript. ESLint parses, traverses ASTs, and runs rules in JavaScript. Biome does it in Rust.
- Parallel processing. Biome uses all CPU cores. ESLint is single-threaded (though ESLint 9 added some parallelism).
- Single parse. Biome parses each file once for both linting and formatting. ESLint + Prettier parse files separately.
- No plugin overhead. ESLint loads and initializes plugins at startup. Biome's rules are compiled into the binary.
Setup
# Install
bun add -d @biomejs/biome
# Initialize config
npx @biomejs/biome init
This creates biome.json:
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"semicolons": "always",
"trailingCommas": "all"
}
}
}
// package.json scripts
{
"scripts": {
"lint": "biome check .",
"lint:fix": "biome check --write .",
"format": "biome format --write ."
}
}
# Lint and format
npx @biomejs/biome check . # Report issues
npx @biomejs/biome check --write . # Fix all auto-fixable issues
npx @biomejs/biome format --write . # Format only
npx @biomejs/biome lint . # Lint only
Rule Coverage
Biome has been steadily implementing ESLint rules. Here's where it stands:
// biome.json — configuring specific rules
{
"linter": {
"rules": {
"recommended": true,
"complexity": {
"noForEach": "warn",
"useSimplifiedLogicExpression": "error"
},
"suspicious": {
"noExplicitAny": "error",
"noConsoleLog": "warn"
},
"correctness": {
"noUnusedVariables": "error",
"noUnusedImports": "error",
"useExhaustiveDependencies": "warn"
},
"style": {
"noNonNullAssertion": "warn",
"useConst": "error",
"useTemplate": "error"
},
"nursery": {
"useSortedClasses": "warn"
}
}
}
}
Rules Biome has:
- Most ESLint core rules
- Most
@typescript-eslintrules - React hooks rules (
useExhaustiveDependencies) - Import sorting
- Tailwind class sorting (nursery)
- Accessibility rules (JSX a11y equivalents)
- Some niche ESLint plugin rules
- Full parity with
eslint-plugin-react(partial coverage) - Some
@typescript-eslinttype-aware rules (requires type information from TS compiler) - Plugin ecosystem — you can't write custom Biome rules in JavaScript
Migration from ESLint
Biome includes a migration tool:
# Automatically convert ESLint config to Biome config
npx @biomejs/biome migrate eslint --write
# Convert Prettier config too
npx @biomejs/biome migrate prettier --write
The migration reads your .eslintrc (or eslint.config.js) and .prettierrc, maps rules to Biome equivalents, and updates biome.json. Rules without Biome equivalents are listed so you can decide what to do.
- Install Biome alongside ESLint (don't remove ESLint yet)
- Run
biome migrate eslint --write - Run
biome check .and compare output toeslint . - Identify any rules that ESLint catches but Biome doesn't
- If the missing rules aren't critical, remove ESLint
- If they are, keep ESLint for those specific rules and use Biome for everything else
Biome vs ESLint Feature Comparison
| Feature | ESLint + Prettier | Biome |
|---|---|---|
| Speed | Slow (~30-60s for large projects) | Fast (~0.5-2s for large projects) |
| Languages | JS, TS (+ plugins for Vue, Svelte, etc.) | JS, TS, JSX, TSX, JSON, CSS |
| Formatting | Prettier (separate tool) | Built-in |
| Config | .eslintrc + .prettierrc + plugins | Single biome.json |
| Plugin system | Massive ecosystem | No custom plugins |
| Type-aware rules | Yes (with @typescript-eslint) | Limited |
| Auto-fix | Most rules | Most rules |
| Editor support | Excellent (VS Code, all editors) | Good (VS Code, IntelliJ) |
| CI integration | Standard | Standard |
| Custom rules | Write your own in JS | Not supported |
| Import sorting | eslint-plugin-import | Built-in |
| Monorepo support | Per-package configs | Cascading configs |
The Plugin Problem
This is the real question. ESLint's power comes from its plugin ecosystem. Your project probably uses some of these:
@typescript-eslint— TypeScript-specific ruleseslint-plugin-react— React ruleseslint-plugin-react-hooks— Hooks ruleseslint-plugin-import— Import ordering and validationeslint-plugin-jsx-a11y— Accessibility ruleseslint-plugin-tailwindcss— Tailwind-specific ruleseslint-plugin-testing-library— Testing library rules
Editor Integration
// .vscode/settings.json
{
// Use Biome as default formatter
"editor.defaultFormatter": "biomejs.biome",
"[javascript]": { "editor.defaultFormatter": "biomejs.biome" },
"[typescript]": { "editor.defaultFormatter": "biomejs.biome" },
"[typescriptreact]": { "editor.defaultFormatter": "biomejs.biome" },
"[json]": { "editor.defaultFormatter": "biomejs.biome" },
"[css]": { "editor.defaultFormatter": "biomejs.biome" },
// Format and fix on save
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"quickfix.biome": "explicit",
"source.organizeImports.biome": "explicit"
}
}
The VS Code extension provides:
- Format on save
- Inline diagnostics (squiggly lines)
- Code actions (quick fixes)
- Import sorting on save
CI Setup
# .github/workflows/lint.yml
name: Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: biomejs/setup-biome@v2
- run: biome ci .
biome ci is the CI-specific command — it reports issues without writing fixes and exits with a non-zero code if there are errors.
When to Switch
Switch to Biome if:- Speed matters to you (especially in CI and pre-commit hooks)
- You want simpler configuration (one tool, one config)
- Your ESLint config uses mostly standard rules
- You're starting a new project
- You depend on specific ESLint plugins that Biome doesn't support
- You've written custom ESLint rules
- You need type-aware linting rules
- Your team's ESLint config is well-established and working fine
- You want Biome for formatting + basic linting (fast)
- You want ESLint for specific plugin rules only (targeted)
// biome.json — handle formatting and basic linting
{
"formatter": { "enabled": true },
"linter": {
"rules": {
"recommended": true,
"correctness": {
"noUnusedVariables": "error",
"noUnusedImports": "error"
}
}
}
}
// eslint.config.js — only the rules Biome can't handle
import tseslint from "typescript-eslint";
export default tseslint.config({
extends: [tseslint.configs.strictTypeChecked],
rules: {
// Only type-aware rules that Biome can't do
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-promises": "error",
"@typescript-eslint/await-thenable": "error",
},
});
The JavaScript tooling ecosystem is consolidating around Rust-based tools. Biome for linting/formatting, Vite (using esbuild/SWC) for building, Turbopack for Next.js. The direction is clear — JavaScript tools written in faster languages. Biome is the most mature entry in the linting space, and it's only getting better. More tooling guides at CodeUp.