| | |

Biome 01 🧹 Why Biome — One tool instead of two, written in Rust

⏱️ Estimated read: 12 min | |

The JavaScript tooling ecosystem has spent a decade accumulating layers. Prettier formats the code, ESLint lints it, eslint-config-prettier disables the overlapping rules, and a small forest of plugins wires the two together. The setup works, but it is two separate tools, two configuration files, two parse passes, and two Node.js processes that each pay the startup cost. Biome is the answer to the question that the accumulated layers provoke: why does checking the code style require two tools when one tool could do both?

Biome started as a fork of Rome, the project that Meta abandoned in 2023, and the community rewrote the governance and shipped it as an independent toolchain . It is written in Rust, it ships as a single binary, and it parses the source file once and reuses the same AST for both the linting and the formatting . The single parse is the architectural decision that makes the rest possible: the linter and the formatter read from the same tree, the same configuration file governs both, and the same CLI command runs both. The formatter scores 97% compatibility with Prettier, and the linter ships over 750 rules ported from ESLint, typescript-eslint, and the popular plugins .

Key point: Biome replaces both the formatter and the linter with a single Rust binary. It parses the source once, applies the lint rules and the format rules from the same AST, and is roughly 25 times faster than Prettier and 15 times faster than ESLint in the official benchmarks . The compatibility is high but not perfect, and the ecosystem is narrower than ESLint’s plugin ecosystem.


Why Biome exists

The two-tool problem. Prettier and ESLint answer different questions, and the separation was the original design. Prettier owns the formatting, ESLint owns the correctness, and eslint-config-prettier disables the ESLint formatting rules so the two do not fight. The separation works, but it means two dependencies, two configuration files, two CLI commands, and two parse passes. The Biome comparison page states the difference plainly: “Biome can replace both your formatter and your linter” . The replacement is the value proposition, and the single binary is the mechanism.

The speed problem. The speed difference is not a marginal improvement. The official benchmark measures Biome formatting 171,127 lines across 2,104 files in 0.41 seconds, while Prettier takes 4.20 seconds—roughly 35 times faster . The linting comparison measures Biome at roughly 15 times faster than ESLint . The speed comes from three sources: the compiled Rust binary that starts without a Node.js runtime, the single parse pass that both the linter and the formatter share, and the parallel file processing that the Rust core performs by default . The speed matters most in the pre-commit hook and the CI job, where the formatting and the linting are the gates that the developer waits on.

The configuration problem. A typical ESLint setup wires together a parser, the TypeScript plugin, the framework plugins, the library plugins, and eslint-config-prettier. Biome needs one package, @biomejs/biome, and an optional biome.json for the customization . The check command runs the formatting, the linting, and the import organization in a single pass . The simplicity is the trade-off against the ecosystem: ESLint has a decade of plugins that Biome cannot run, and the Biome plugins are written in GritQL, a pattern language, rather than JavaScript .

The compatibility problem. The formatter matches Prettier’s output in 97% of cases, and it won the Prettier challenge that Christopher Chedeau, one of Prettier’s creators, launched . The 3% difference is intentional, and the differences are documented. The default options also differ: Biome indents with tabs while Prettier uses spaces . The linter is not a drop-in replacement for ESLint: the rule names use camelCase rather than kebab-case, some rules support fewer options, and the type-aware linting has a smaller rule count than typescript-eslint . The compatibility is high enough for a new project, and the gap is visible for a project with a heavy ESLint configuration.

The trade-off. Biome is not a drop-in replacement for every ESLint configuration. The plugin ecosystem is the gap: ESLint’s JavaScript plugins cannot run in Biome, and the Biome plugins use the different GritQL language . The type-aware rules are newer and fewer than typescript-eslint‘s. The HTML and the Vue support are less stable than Prettier’s . A project that depends on a specific ESLint plugin—the React hooks rules, the framework-specific rules—may need to keep ESLint alongside Biome, or wait for the plugin gap to close.


a. The formatter and the Prettier compatibility

The Biome formatter is built to be a drop-in replacement for Prettier, and the compatibility is measured at 97% . The compatibility means that a project can adopt Biome without a formatting churn commit that rewrites every file. The biome migrate prettier command reads the existing .prettierrc and the .prettierignore and ports the options and the globs into the biome.json . The migration is the mechanism that makes the switch practical for an existing project.

// biome.json — the formatter configuration
{
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 80
  }
}

The indentStyle and the indentWidth are the formatting options that mirror Prettier’s useTabs and tabWidth. The default indentStyle is "tab", which differs from Prettier’s default of spaces, so a project that wants the Prettier-like output sets "indentStyle": "space" explicitly . The lineWidth mirrors Prettier’s printWidth. The formatter configuration is the subset of Prettier’s options that Biome supports, and the migration command is the mapping between them.

// The format command
npx @biomejs/biome format --write ./src

The format command formats the files and writes the changes. The --write flag is the mechanism that applies the changes; without it, the command reports the differences without modifying the files. The formatter is the half of Biome that replaces Prettier, and the 97% compatibility is the reason the replacement does not produce a formatting churn.


b. The linter and the ESLint migration

The Biome linter is not a drop-in replacement for ESLint, but it ships 750 rules ported from ESLint and the popular plugins, and it covers the common cases . The biome migrate eslint command reads the ESLint configuration and ports the rules into the biome.json . The migration is the starting point, and the manual review is the step that catches the rules that have different names or fewer options.

// biome.json — the linter configuration
{
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "correctness": {
        "noUnusedVariables": "error"
      },
      "suspicious": {
        "noDoubleEquals": "error"
      }
    }
  }
}

The noDoubleEquals is the Biome name for the ESLint eqeqeq rule, and the noUnusedVariables is the Biome name for the no-unused-vars rule . The naming convention is the camelCase rather than the ESLint’s kebab-case, and the migration command maps the names. The recommended flag enables the recommended rules, which is the default .

# The lint command
npx @biomejs/biome lint --write ./src

# The check command (lint + format + import sorting)
npx @biomejs/biome check --write ./src

The lint command applies the lint rules and the safe fixes. The check command runs the linting, the formatting, and the import organization in a single pass . The check is the command that a pre-commit hook or a CI job uses, because it covers all three concerns in one invocation.


Complete Example Session

// ============================================
// PART 1: THE INSTALLATION
// ============================================

// npm install --save-dev --save-exact @biomejs/biome

// The single package replaces the ESLint and Prettier dependencies.

// ============================================
// PART 2: THE INITIALIZATION
// ============================================

// npx @biomejs/biome init

// The command creates the biome.json file.
// The default configuration enables the formatter and the linter.

// ============================================
// PART 3: THE BIOME.JSON
// ============================================

// biome.json
{
  "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  }
}

// The formatter is enabled by default.
// The linter is enabled by default.
// The recommended rules are on.

// ============================================
// PART 4: THE CHECK COMMAND
// ============================================

// npx @biomejs/biome check --write ./src

// The command runs:
// - The formatter
// - The linter
// - The import organizer

// The --write flag applies the safe fixes.

// ============================================
// PART 5: THE ESLINT MIGRATION
// ============================================

// npx @biomejs/biome migrate eslint --write

// The command reads the ESLint configuration.
// It ports the rules to the biome.json.
// The manual review catches the renamed rules.

// ============================================
// PART 6: THE PRETTIER MIGRATION
// ============================================

// npx @biomejs/biome migrate prettier --write

// The command reads the .prettierrc.
// It ports the options to the biome.json.
// The formatter matches Prettier in 97% of cases.

// ============================================
// PART 7: THE CI COMMAND
// ============================================

// npx @biomejs/biome ci ./src

// The ci command is the read-only check for the CI.
// It fails if the code is not formatted or the lint rules fail.

// ============================================
// PART 8: THE PERFORMANCE
// ============================================

// The official benchmark:
// Biome formats 2,104 files in 0.41s.
// Prettier formats the same files in 4.20s.
// The difference is roughly 35x.

// ============================================
// PART 9: THE COMPATIBILITY
// ============================================

// The formatter: 97% Prettier compatibility.
// The linter: 750+ rules from ESLint and typescript-eslint.
// The plugin ecosystem: narrower than ESLint's.

// ============================================
// PART 10: THE CHECKLIST
// ============================================

// [ ] The @biomejs/biome package is installed.
// [ ] The biome.json is created with the init command.
// [ ] The indentStyle is set to "space" for the Prettier-like output.
// [ ] The migrate eslint command ports the ESLint rules.
// [ ] The migrate prettier command ports the Prettier options.
// [ ] The check command runs the formatter, the linter, and the import organizer.
// [ ] The ci command is used in the CI pipeline.

The ten parts covered the installation, the initialization, the configuration, the check command, the ESLint migration, the Prettier migration, the CI command, the performance, the compatibility, and the checklist. The through-line is that Biome is one binary, one parse, and one configuration, and the compatibility is high enough to replace two tools.


Quick Reference

The Biome vs the alternatives

AspectBiomeESLint + Prettier
ToolsOne binaryTwo tools
LanguageRustJavaScript
Parse passesOneTwo
ConfigurationOne biome.jsonTwo config files + plugins
Formatter compatibility97% Prettier—
Linter rules750+More, with the plugins
Speed (format)~35× faster than Prettier—
Speed (lint)~15× faster than ESLint—

The CLI commands

CommandPurpose
biome initCreate the biome.json
biome format --writeFormat the files
biome lint --writeLint and apply the safe fixes
biome check --writeFormat, lint, and organize the imports
biome ciThe read-only CI check
biome migrate eslintPort the ESLint configuration
biome migrate prettierPort the Prettier configuration

The configuration

OptionDefaultPrettier equivalent
indentStyle"tab"useTabs: true
indentWidth2tabWidth: 2
lineWidth80printWidth: 80

The compatibility gaps

GapDetail
Plugin ecosystemESLint’s JavaScript plugins cannot run in Biome
HTML/Vue supportLess stable than Prettier’s
Type-aware rulesFewer than typescript-eslint
Rule namescamelCase rather than kebab-case

Best Practices

✅ Do This:

# Use the single package
npm install -D @biomejs/biome                                    # ✅

# Use the check command for the pre-commit hook
npx @biomejs/biome check --write ./src                           # ✅

# Use the migration commands for the existing config
npx @biomejs/biome migrate eslint --write                        # ✅

❌ Don’t Do This:

# Assume the ESLint plugins run in Biome
# The plugin ecosystem is the gap.                              # ❌

# Expect the exact Prettier output
# The 3% difference is intentional.                             # ❌

Common Pitfalls

PitfallWhy It HappensFix
The formatting differs from PrettierThe indentStyle default is tabSet "indentStyle": "space"
The ESLint plugin rule is missingThe plugin ecosystem is narrowerKeep ESLint for the plugin-only rules
The migration misses a ruleThe rule name differsReview the migrated biome.json
The CI fails on the unformatted codeThe ci command is strictRun check --write locally first

Real-World Examples

1. The installation

npm install -D @biomejs/biome

2. The initialization

npx @biomejs/biome init

3. The format command

npx @biomejs/biome format --write ./src

4. The lint command

npx @biomejs/biome lint --write ./src

5. The check command

npx @biomejs/biome check --write ./src

6. The CI command

npx @biomejs/biome ci ./src

7. The ESLint migration

npx @biomejs/biome migrate eslint --write

8. The Prettier migration

npx @biomejs/biome migrate prettier --write

9. The configuration

{ "formatter": { "indentStyle": "space" } }

10. The rule name

{ "linter": { "rules": { "suspicious": { "noDoubleEquals": "error" } } } }

Visual

The Two-Tool vs the One-Tool

┌──────────────────────────────────────────────────────────────┐
│  ESLINT + PRETTIER VS BIOME                                  │
│                                                              │
│  ESLint + Prettier              Biome                       │
│  ┌─────────────────────┐       ┌─────────────────────┐     │
│  │ ESLint (Node.js)    │       │ One Rust binary     │     │
│  │ Prettier (Node.js)  │       │ One parse pass      │     │
│  │ Two config files    │       │ One biome.json      │     │
│  │ Two parse passes    │       │ One CLI command     │     │
│  └─────────────────────┘       └─────────────────────┘     │
│                                                              │
│  The two tools answer different questions.                  │
│  The one tool answers both from the same AST.               │
└──────────────────────────────────────────────────────────────┘

The Speed Difference

┌──────────────────────────────────────────────────────────────┐
│  THE OFFICIAL BENCHMARK                                      │
│                                                              │
│  Format (2,104 files):                                       │
│  ┌─────────────────────┐                                    │
│  │ Prettier: 4.20s     │                                    │
│  │ Biome:    0.41s     │  ~35x faster                       │
│  └─────────────────────┘                                    │
│                                                              │
│  Lint:                                                       │
│  ┌─────────────────────┐                                    │
│  │ ESLint: 8-10s       │                                    │
│  │ Biome:  0.5s        │  ~15-20x faster                    │
│  └─────────────────────┘                                    │
│                                                              │
│  The speed comes from the Rust binary, the single parse,    │
│  and the parallel processing.                               │
└──────────────────────────────────────────────────────────────┘

The Compatibility Gap

┌──────────────────────────────────────────────────────────────┐
│  WHAT BIOME REPLACES AND WHAT IT DOES NOT                    │
│                                                              │
│  ✅ Replaces:                                                 │
│  ┌─────────────────────┐                                    │
│  │ Prettier formatter  │  97% compatibility                 │
│  │ ESLint core rules   │  750+ rules ported                 │
│  │ Import sorting      │  Built-in                          │
│  └─────────────────────┘                                    │
│                                                              │
│  ❌ Does not replace:                                         │
│  ┌─────────────────────┐                                    │
│  │ ESLint JS plugins   │  The plugin ecosystem gap          │
│  │ Type-aware rules    │  Fewer than typescript-eslint      │
│  │ HTML/Vue support    │  Less stable than Prettier         │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
BiomeOne Rust binary for the formatting and the linting
Formatter compatibility97% Prettier
Linter rules750+ from ESLint and typescript-eslint
Speed (format)~35× faster than Prettier
Speed (lint)~15× faster than ESLint
ConfigurationOne biome.json
CLI commandcheck runs format + lint + import sorting
Plugin ecosystemNarrower than ESLint’s

Key takeaways:

  • Biome replaces both the formatter and the linter with one binary. The single Rust binary parses the source once, applies the lint and the format rules from the same AST, and runs both from the same check command . The single parse is the architectural decision that makes the speed and the simplicity possible.
  • The formatter matches Prettier in 97% of cases. The compatibility is high enough to avoid a formatting churn commit, and the migrate prettier command ports the existing options . The default indentStyle is tab, so a project that wants the Prettier-like output sets "indentStyle": "space" explicitly.
  • The linter ships 750+ rules ported from ESLint and the popular plugins. The migrate eslint command ports the existing configuration, but the rule names use camelCase and some rules support fewer options . The migration is the starting point, and the manual review catches the differences.
  • The speed is the headline benefit. Biome formats roughly 35 times faster than Prettier and lints roughly 15 times faster than ESLint . The speed matters most in the pre-commit hook and the CI job, where the formatting and the linting are the gates that the developer waits on.
  • The plugin ecosystem is the gap. ESLint’s JavaScript plugins cannot run in Biome, and the Biome plugins use the GritQL language . A project that depends on a specific ESLint plugin may need to keep ESLint alongside Biome for the plugin-only rules.

Remember: Biome is the answer to the two-tool problem. It is one Rust binary that formats, lints, and organizes the imports from a single parse, with 97% Prettier compatibility and 750+ ESLint rules. The speed is roughly 35 times faster for the formatting and 15 times faster for the linting. The plugin ecosystem is narrower, and the migration commands are the starting point for the switch. The one tool replaces the two, and the single parse is the reason.

Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!