Choose Biome when you want one fast binary doing both linting and formatting with a single config file, and your rule needs are covered by its built-in set. Choose ESLint + Prettier when you depend on framework or accessibility plugins, deep type-aware TypeScript rules, or custom in-house rules. Both are legitimate; the deciding factor is ecosystem breadth versus setup simplicity and speed.
The JavaScript ecosystem spent a decade settling on a two-tool answer: ESLint finds problems, Prettier fixes layout, and a small amount of glue keeps them from fighting. Biome challenges that by collapsing both jobs into one Rust binary with one config. This comparison weighs them on the criteria that actually change your day — rule coverage, speed, configuration burden, CI behavior, and what it costs to switch.
What are these tools, precisely?
ESLint is a pluggable linter for JavaScript and TypeScript. It parses your code into an abstract syntax tree and runs rules against it, reporting probable bugs, banned constructs, and convention violations — many with autofix. Its power comes from plugins: the typescript-eslint project adds TypeScript parsing and type-aware rules, and separate plugins cover React, Vue, Svelte, Node, imports, accessibility, testing libraries, and more. Since v9 the default configuration format is flat config (eslint.config.js), a plain JavaScript file exporting an array of config objects.
Prettier is an opinionated formatter. It reprints your source from the parsed tree using one canonical layout, deliberately exposing few options so that layout stops being a decision. It reports no bugs at all — that is not its job. If the linter/formatter split is fuzzy for you, our guide on whether you need a code formatter draws the line carefully.
Biome is a toolchain written in Rust that does both: a formatter designed to be highly compatible with Prettier's output, plus a linter whose rules are drawn from ESLint, typescript-eslint, and other sources and given its own naming scheme. It is configured by a single biome.json and driven by one command, biome check, which lints and formats in one pass.
How do they compare on the criteria that matter?
| Criterion | Biome | ESLint + Prettier |
|---|---|---|
| Tools to install and configure | One binary, one config file | Two tools, two configs, plus glue packages |
| Implementation | Rust, parallelized | JavaScript on Node |
| Speed on large repos | The project's headline claim; generally the faster option | Adequate; the slowest part is usually type-aware linting |
| Rule ecosystem | Substantial built-in set, no comparable third-party plugin market | The decisive advantage — plugins for nearly every framework, and custom rules are routine |
| Type-aware TypeScript rules | Partial and evolving; some rules work without a full type check | Mature via typescript-eslint, at a real speed cost |
| Formatting fidelity | Aims at high Prettier compatibility, not byte-identical everywhere | The reference implementation |
| Language coverage | JS, TS, JSX, JSON, CSS, and a growing list | JS/TS via ESLint; Prettier also handles Markdown, YAML, HTML, and more |
| Config surface | One JSON file, schema-validated | Flat config JS file plus .prettierrc, with more knobs |
| Maturity and hiring familiarity | Younger project, moving fast | Years of production use; most developers already know it |
| Migration in | biome migrate eslint / migrate prettier |
The default most repos already have |
Read that table as a trade between breadth and simplicity. Nothing in Biome's design prevents it from covering a rule you need — it just may not have that rule today, and there is no plugin you can install to add it in the way ESLint has trained the ecosystem to expect. (Biome has been adding plugin support, but it is not the mature marketplace ESLint has.) Conversely, nothing about ESLint + Prettier is broken — it is simply two tools, two dependency trees, and a Node process where Biome runs a compiled binary.
What does each configuration actually look like?
A workable Biome setup, with formatter and recommended lint rules on:
{
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"linter": {
"enabled": true,
"rules": { "recommended": true }
},
"javascript": {
"formatter": { "quoteStyle": "single", "semicolons": "always" }
}
}
Point $schema at the version you actually installed — the schema URL is version-pinned, and Biome moves quickly. Then:
npx @biomejs/biome check --write .
The equivalent ESLint flat config for a TypeScript project:
// eslint.config.js
import js from "@eslint/js";
import tseslint from "typescript-eslint";
export default tseslint.config(
js.configs.recommended,
tseslint.configs.recommended,
{ ignores: ["dist/**", "coverage/**"] }
);
With a .prettierrc alongside it:
{
"singleQuote": true,
"printWidth": 100,
"semi": true
}
Two commands instead of one:
npx eslint . && npx prettier --check .
Note the shape of the difference. Biome's single config is easier to reason about; ESLint's is a JavaScript module, which means it can compose, import shared configs, and apply different rules to different globs with full programmatic control. That flexibility is exactly what large monorepos tend to need — and exactly what a small app does not.
Which should you pick for your situation?
Pick Biome if you are starting a new JS/TS project, you value a one-command setup, lint and format time is a felt pain in your feedback loop, and a scan of its rule list covers what you enforce. Teams on large repos where CI lint time is the binding constraint have the strongest case.
Pick ESLint + Prettier if you rely on framework-specific plugins (React hooks rules, Vue or Svelte support, jsx-a11y), you enforce architectural constraints with custom rules, you need the full depth of type-aware TypeScript linting, or you format languages outside Biome's current coverage. Also pick it if "boring and universally understood" has value to you — it usually does.
A defensible middle path: use Biome as your formatter and fast baseline linter, and keep ESLint for the plugin-specific rules only, with its formatting-adjacent rules turned off. You get most of the speed win without giving up the plugins. The cost is that you are back to two tools — the exact thing Biome exists to avoid — so only take this route if the plugin dependency is real.
Whichever you choose, run it in more than one place. The three-layer pattern — editor feedback, a pre-commit hook, and a CI gate that fails the build — is what makes any of this stick, and it works identically for both toolchains. And whichever you adopt, keep formatting out of your linter's hands; letting two tools referee whitespace is the classic way to make both worse, as our linting primer explains.
The verdict
For new projects with mainstream needs, Biome is the better default: one install, one config, one command, and a noticeably tighter feedback loop. For existing projects with an established plugin footprint, the honest answer is usually to stay on ESLint + Prettier — the migration cost is real and the ecosystem gap is the one thing a faster binary cannot close for you. Run biome migrate eslint --write on a branch, look at what it can and cannot translate, and let that diff decide rather than the benchmark headline.
The same reasoning generalizes past JavaScript: Python is seeing the same consolidation with Ruff. Our guide to choosing a linter for your language walks the equivalent decision in Go, Rust, and Python.
Tooling in this space moves fast — rule counts, plugin support, and type-aware capabilities all change between releases, so check each project's current documentation before you finalize a choice.
FAQ
Can I use Biome's formatter with ESLint's linter? Yes, and it is a common hybrid. Run Biome for formatting, disable ESLint's stylistic and formatting-related rules so the two never conflict, and keep ESLint only for the plugin rules you actually need. It is more moving parts than either pure option, but it is a legitimate incremental step.
Is Biome's output identical to Prettier's? Biome targets high compatibility with Prettier's formatting, and its documentation records the specific cases where it intentionally or currently differs. Expect the vast majority of a codebase to reformat identically and a small tail not to. Reformat on a dedicated commit so the diff is reviewable in isolation.
Does Biome replace TypeScript's type checker?
No. Neither Biome nor ESLint replaces tsc. Type checking is a distinct analysis that verifies types line up across your whole program, and you should keep running it in CI regardless of which linter you choose.
How hard is migrating from ESLint and Prettier to Biome?
Biome ships biome migrate eslint and biome migrate prettier commands that translate existing configuration into biome.json. Config translation is usually quick; the real work is auditing which plugin rules have no Biome equivalent and deciding whether you can live without them.
Is ESLint being replaced? No. ESLint remains actively developed and is the most widely deployed JS/TS linter, and its plugin ecosystem is the reason. Biome is a genuine alternative for a large set of projects, not a successor.
Ready to commit? Compare the leading JavaScript and TypeScript linters side by side on Lintense before you standardize your team on one.