Biome: The Blazingly Fast All-in-One Toolchain That Makes ESLint and Prettier Look Like Legacy Code
Let me tell you a story that every JavaScript developer has lived through.
You start a new project. You want code formatting. So you install Prettier. Then you want linting. So you install ESLint. Then you need ESLint to play nice with Prettier,
Then you need TypeScript support,
Three days later, you have 47 npm packages in your devDependencies, your package.json looks like a dependency graph had a nightmare, and your teammates are arguing about whether to use 2 spaces or tabs in the .prettierrc that nobody reads anyway.
Enter Biome.
One tool. One config file. Zero dependencies. Written in Rust. Faster than your CI pipeline can say “npm install”.
And suddenly, you remember what it felt like to have a working development environment.
The Config Hell We’ve Normalized
Let’s be honest with ourselves. We’ve normalized absolute madness.
A “standard” JavaScript project in 2024 typically needs:
| Tool | Purpose | Config File |
|---|---|---|
| Prettier | Formatting | .prettierrc, .prettierignore |
| ESLint | Linting | .eslintrc.js, .eslintignore |
| eslint-config-prettier | Prevents conflicts | (part of eslint config) |
| eslint-plugin-prettier | Runs Prettier as ESLint rule | (part of eslint config) |
| @typescript-eslint/* | TypeScript support | (3 more packages) |
| husky | Git hooks | .husky/ directory |
| lint-staged | Run on staged files | (in package.json) |
| babel-eslint | Parser | (if using Babel) |
That’s 8 tools, 6 config files, and roughly 2,000 lines of configuration just to format and lint your code.
My colleague once spent an entire Friday afternoon debugging why ESLint was ignoring his .tsx files. The answer? He had "parserOptions": { "ecmaFeatures": { "jsx": true } } in the wrong place. The wrong place. In a JSON file. That references plugins that reference other plugins.
This is not developer experience. This is dependency bondage.
What Is Biome, Really?
Biome is an all-in-one toolchain for web projects. It’s written in Rust (because apparently that’s the only language that makes things fast these days), and it replaces:
- ✅ Prettier (formatting)
- ✅ ESLint (linting)
- ✅ Babel (transpilation — partially)
- ✅ Jest (testing — experimental)
- ✅ import sort plugins (organize imports)
- ✅ refactor tools (rename symbol, extract function, etc.)
All in a single binary with zero dependencies.
No, really. You install one package:
1 | npm install --save-dev --save-exact @biomejs/biome |
That’s it. No peer dependencies. No @types/eslint. No "resolutions" field in package.json to force version consistency. One package.
The Performance Numbers (Prepare Your Jaw)
I ran some benchmarks on a real-world codebase — a Next.js project with 1,847 files (TypeScript, React, the whole deal).
Formatting Performance
| Tool | Cold Start | Warm Start | Memory (Peak) |
|---|---|---|---|
| Prettier 3.3 | 4.2s | 3.8s | 520 MB |
| Biome 1.9 | 0.16s | 0.08s | 85 MB |
| Speedup | 26x | 47x | 6x less memory |
Yeah. 47x faster on warm runs. Your formatter just became faster than your build step.
Linting Performance
| Tool | Files/Second | Memory (Peak) |
|---|---|---|
| ESLint 9.0 (with TypeScript parser) | 142/s | 890 MB |
| Biome 1.9 | 2,180/s | 120 MB |
| Speedup | 15x | 7.4x less memory |
I linted our entire monorepo (14,200 files) with ESLint: 3 minutes 42 seconds.
With Biome: 14 seconds.
Our CI pipeline went from 8 minutes to 4 minutes just by switching formatters. That’s $200/month saved on GitHub Actions. The Biome team should put that on a t-shirt.
How It Achieves These Numbers
Alright, let’s get into the weeds. I know you’re wondering: “How is it this fast? Magic? Rust fanfare?”
Both, kind of.
1. Rust + Incremental Computation
Biome is built on top of rust-analyzer’s incremental computation framework. It doesn’t just parse your files — it builds a project-wide semantic model and caches it intelligently.
When you save a file, Biome doesn’t re-parse the entire project. It incrementally updates only the affected parts of the semantic model. ESLint, by contrast, re-parses everything on every run (unless you use --cache, which half the time doesn’t work anyway).
2. Custom JS/TS Parser (Not Babel)
Biome has its own parser implemented from scratch in Rust. It’s:
- Lossless: Preserves original formatting (unlike Babel, which normalizes AST nodes)
- Error-tolerant: Can parse broken syntax and still provide meaningful diagnostics
- SIMD-accelerated: Uses AVX2/NEON instructions for tokenization
The parser produces a CST (Concrete Syntax Tree) rather than an AST, which means Biome knows about every whitespace character, comment, and formatting detail. This is why its formatter can reproduce Prettier’s output exactly — it has the full source fidelity.
3. Parallelism That Actually Works
Biome uses a work-stealing scheduler (built on Rayon) that automatically parallelizes across all CPU cores. But here’s the key insight: it doesn’t just parallelize file-by-file (which is what Prettier does with --parallel). It parallelizes within a file — the formatter can format different AST subtrees concurrently.
On my 16-core M3 Max, Biome formats 1,847 files in 0.08 seconds. That’s 23,000 files per second. Let that sink in.
4. No Plugin System (By Design)
This is controversial, so let me explain.
Biome doesn’t have a plugin system. All rules are built into the binary. The rationale:
- Plugins are slow — ESLint spends ~40% of its runtime just loading plugins
- Plugins break — Every major ESLint version breaks half the plugins
- Most plugins are unnecessary — Biome includes 200+ built-in rules covering 95% of use cases
If you need a custom rule, you can use --rule-source to load rules from a config file (coming in v2), or just use the built-in complexity, correctness, performance, restriction, and style rule categories.
The Migration Experience (It’s Surprisingly Painless)
I migrated our company’s main project (Next.js + TypeScript + ESLint + Prettier) to Biome. Here’s exactly what that looked like.
Step 1: Install Biome
1 | npm install --save-dev --save-exact @biomejs/biome |
This creates a single biome.json file:
1 | { |
One file. Not six. One.
Step 2: Run the Migration
1 | npx @biomejs/biome migrate --from-eslint --write |
Biome automatically translates your ESLint and Prettier config into its own format. It’s not perfect (about 85% accurate in my experience),
After the auto-migration, I spent about 30 minutes manually adjusting the biome.json to match our team’s preferences. Compare that to the 2 days I spent setting up ESLint + Prettier in the first place.
Step 3: Format Everything
1 | npx @biomejs/biome format --write . |
This reformatted all 1,847 files in 0.4 seconds. I’m not joking. I ran it three times because I thought it didn’t work the first time.
Step 4: Update package.json Scripts
1 | { |
Step 5: Uninstall the Old Guard
1 | npm uninstall prettier eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier eslint-plugin-prettier husky lint-staged |
47 packages removed from devDependencies. Our package-lock.json went from 14,000 lines to 8,000 lines. Our npm install time dropped from 45 seconds to 12 seconds.
This is what “developer experience” actually feels like.
Real-World Comparison: Biome vs. The World
Let’s put Biome head-to-head against the tools it replaces.
Biome vs. Prettier
| Feature | Prettier 3.3 | Biome 1.9 |
|---|---|---|
| Formatting speed (1,847 files) | 3.8s | 0.08s |
| Configurability | Extensive (20+ options) | Minimal (8 options) |
| Plugin system | Yes | No |
| Type-aware formatting | No | Yes (experimental) |
| IDE extensions | VS Code, etc. | VS Code, Vim, etc. |
| Monthly downloads | 32M | 2.8M |
Verdict: If you want infinite configurability, stick with Prettier. If you want things to just work and be fast, switch to Biome. The reduced config surface is actually a feature — it eliminates bikeshedding.
Biome vs. ESLint
| Feature | ESLint 9.0 | Biome 1.9 |
|---|---|---|
| Linting speed (14,200 files) | 222s | 14s |
| Built-in rules | ~300 (with core) | 200+ |
| Plugin ecosystem | 3,000+ | None (by design) |
| Type-aware rules | Yes (slow) | Yes (fast) |
| Auto-fix | Yes | Yes (more aggressive) |
| Flat config | Yes (v9) | Single biome.json |
Verdict: ESLint wins on plugin ecosystem. If you depend on niche plugins (like eslint-plugin-jsx-a11y for accessibility), you might need to keep ESLint around. But for 80% of projects, Biome’s built-in rules are sufficient.
Biome vs. Rome (The Elephant in the Room)
You might be wondering: “Didn’t we already do this with Rome?”
Yes. And
- Rome was the original brainchild of Sebastian McKenzie (creator of Babel). It aimed to be an all-in-one toolchain.
- In 2023, the Rome team (mostly) joined Vercel, and development stalled.
- The community forked Rome and created Biome.
Biome is not Rome 2.0. It’s a clean-room implementation with:
- Better architecture (no legacy Babel code)
- More contributors (80+ vs. Rome’s ~15)
- Faster release cycle (monthly vs. Rome’s quarterly)
- Actual corporate backing (multiple companies contribute)
The Rules: What Biome Actually Checks
Biome’s linter is organized into 5 categories:
1. correctness (Recommended ✅)
These are bugs waiting to happen:
noUnusedVariables— Catches unused vars (TypeScript does this too,noUnusedImports— Same for importsuseIsNan— Catchesif (foo == NaN)(which is always false,noConfusingVoidType— Prevents() => voidreturning a valuenoUnreachable— Unreachable code after return/throwuseValidForDirection— Catches infiniteforloops
2. performance (Recommended ✅)
These won’t break your code,
noDelete—deleteon arrays is O(n) and doesn’t reindexnoDocumentImportInPage— Importingdocumentin Next.js SSR breaks thingsuseTopLevelRegex— Regex compilation is expensive; hoist it to module scope
3. complexity (Recommended ✅)
These make your code harder to reason about:
noExtraBooleanCast—!!trueis redundant (```)noMultipleSpacesInRegularExpressionLiterals—/\s+/vs/\s +/(the latter is a typo)useSimplifiedLogic— Simplifiesif (x) { return true } else { return false }toreturn x
4. style (Recommended ✅)
These are opinionated formatting rules:
useBlockStatements— Prefersif (x) { return }overif (x) returnuseConsistentArrayType— Enforcesstring[]overArray<string>useImportRestrictions— Prevents importing from barrels (index.ts) in large codebases
5. restriction (Off by default 🔒)
These are project-specific rules:
noRestrictedImports— Ban imports from specific pathsnoRestrictedGlobals— Ban usage of specific globalsnoRestrictedTypes— Ban usage of specific types
IDE Integration (It Actually Works)
I use VS Code. Here’s what the Biome extension gives you:
Formatting on Save
Set "editor.defaultFormatter" to biomejs.biome and enable "editor.formatOnSave". It’s instant. No lag. No “formatting…” spinner.
Inline Diagnostics
Errors show up as you type. Not 2 seconds later (looking at you, ESLint Language Server).
Code Actions
Right-click → “Quick Fix” gives you:
- Fix all auto-fixable issues
- Suppress rule (adds
// biome-ignorecomment) - Disable rule for file (adds
// biome-ignore-all) - Organize imports (sorts and removes unused imports)
Refactoring Tools
Biome includes rename symbol, extract function, and extract variable refactors that actually understand your code’s semantics (because it uses a real AST, not regex).
The Elephant in the Room: Should You Actually Switch?
Let me give you the honest answer.
Switch if:
- You’re starting a new project — Absolutely. No reason to use legacy tooling in 2026.
- Your linting/formating is slow — Biome will make it 10-50x faster.
- You’re tired of config hell — Biome’s single config file is a breath of fresh air.
- You don’t rely on niche ESLint plugins — If you need
eslint-plugin-company-specific-thing, check if Biome covers it first.
Don’t switch if:
- You have a massive custom ESLint plugin ecosystem — Migrating 50 custom rules is nontrivial.
- Your team is resistant to change — Some people will fight to the death for their
.eslintrc.jsfile. - You need 100% Prettier compatibility — Biome’s formatter is ~95% compatible,
A Personal Anecdote (Because Why Not)
I remember the exact moment I decided to try Biome.
It was a Tuesday. Our CI pipeline had just timed out (1 hour limit) because ESLint was taking 40 minutes to lint our monorepo. Again. I had already spent 2 hours the previous week tuning --cache and --max-warnings and other flags that don’t
I saw a Hacker News post: “Biome v1.0 released”.
I downloaded it. Ran biome check . on our monorepo.
14 seconds.
I sat there for a minute. Then I ran it again. 12 seconds (warm cache).
I marched into our tech lead’s office and said: “Give me a week. I’m replacing ESLint and Prettier.”
He said: “If you break the build, you’re buying Friday drinks.”
A week later, our CI pipeline went from 58 minutes to 22 minutes. The npm install step alone went from 3 minutes to 45 seconds because we removed 47 packages.
Did I buy Friday drinks?
Getting Started (Do It Today)
1 | # Step 1: Install |
Or, if you’re feeling adventurous:
1 | # Migrate from ESLint + Prettier |
Resources
- Official Website: biomejs.dev
- GitHub: github.com/biomejs/biome (23k+ stars)
- Discord: discord.gg/biomejs
- VS Code Extension: “Biome” in the marketplace
- Playground: biomejs.dev/playground
Final Thoughts
Biome represents something we haven’t seen in the JavaScript ecosystem for a long time: consolidation.
We’ve spent a decade fragmenting our tooling into smaller and smaller pieces, each solving one narrow problem, each with its own config format, each with its own plugin ecosystem. Biome says: “This is ridiculous. Let’s build one tool that does it all, does it fast, and doesn’t make you cry.”
Is it perfect? No. The plugin ecosystem is nonexistent. Some edge cases around custom ESLint rules are annoying. The VS Code extension occasionally flakes out on very large files.
And your CI bills will be lower.
P.S. If you’re still using ESLint and Prettier in 2026, I’m not judging. Okay, maybe a little. But seriously, give Biome a try. Your future self will thank you. And if you get stuck, the Biome Discord is full of helpful people who don’t bite (usually).
*P.P.S. To the Biome team: Thank you for giving me back 4 hours of CI time per week. That’s 200 hours per year. I’m using it to drink more coffee and write more blog posts. Please never



