Biome Banner

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:

  1. Plugins are slow — ESLint spends ~40% of its runtime just loading plugins
  2. Plugins break — Every major ESLint version breaks half the plugins
  3. 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
2
npm install --save-dev --save-exact @biomejs/biome
npx @biomejs/biome init

This creates a single biome.json file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentSize": 2
},
"javascript": {
"formatter": {
"semicolons": "always",
"quoteStyle": "double"
}
}
}

One file. Not six. One.

Step 2: Run the Migration

1
2
npx @biomejs/biome migrate --from-eslint --write
npx @biomejs/biome migrate --from-prettier --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
2
3
4
5
6
7
{
"scripts": {
"format": "biome format --write .",
"lint": "biome check --apply .",
"check": "biome check ."
}
}

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:

These are bugs waiting to happen:

  • noUnusedVariables — Catches unused vars (TypeScript does this too,
  • noUnusedImports — Same for imports
  • useIsNan — Catches if (foo == NaN) (which is always false,
  • noConfusingVoidType — Prevents () => void returning a value
  • noUnreachable — Unreachable code after return/throw
  • useValidForDirection — Catches infinite for loops

These won’t break your code,

  • noDeletedelete on arrays is O(n) and doesn’t reindex
  • noDocumentImportInPage — Importing document in Next.js SSR breaks things
  • useTopLevelRegex — Regex compilation is expensive; hoist it to module scope

These make your code harder to reason about:

  • noExtraBooleanCast!!true is redundant (```)
  • noMultipleSpacesInRegularExpressionLiterals/\s+/ vs /\s +/ (the latter is a typo)
  • useSimplifiedLogic — Simplifies if (x) { return true } else { return false } to return x

These are opinionated formatting rules:

  • useBlockStatements — Prefers if (x) { return } over if (x) return
  • useConsistentArrayType — Enforces string[] over Array<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 paths
  • noRestrictedGlobals — Ban usage of specific globals
  • noRestrictedTypes — 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-ignore comment)
  • 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:

  1. You’re starting a new project — Absolutely. No reason to use legacy tooling in 2026.
  2. Your linting/formating is slow — Biome will make it 10-50x faster.
  3. You’re tired of config hell — Biome’s single config file is a breath of fresh air.
  4. 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:

  1. You have a massive custom ESLint plugin ecosystem — Migrating 50 custom rules is nontrivial.
  2. Your team is resistant to change — Some people will fight to the death for their .eslintrc.js file.
  3. 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Step 1: Install
npm install --save-dev --save-exact @biomejs/biome

# Step 2: Initialize
npx @biomejs/biome init

# Step 3: Format everything
npx @biomejs/biome format --write .

# Step 4: Check and auto-fix
npx @biomejs/biome check --apply .

# Step 5: Add to package.json
# "format": "biome format --write ."
# "lint": "biome check --apply ."

Or, if you’re feeling adventurous:

1
2
3
4
5
6
# Migrate from ESLint + Prettier
npx @biomejs/biome migrate --from-eslint --write
npx @biomejs/biome migrate --from-prettier --write

# Then uninstall the old tools
npm uninstall prettier eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier eslint-plugin-prettier

Resources


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