Biome — The Rust Toolchain Replacing ESLint + Prettier, 50x Faster

Posted on: 4/26/2026 8:16:03 AM

Are you using ESLint + Prettier and watching your CI lint step take 30-45 seconds? Config files scattered everywhere — .eslintrc.js, .prettierrc, .eslintignore, .prettierignore — and they occasionally conflict with each other? Biome is the answer: a single toolchain written in Rust that lints and formats, 25-50x faster than the traditional ESLint + Prettier combo.

0.8sLint 10,000 files (ESLint: 45s)
0.3sFormat 10,000 files (Prettier: 12s)
491+Built-in lint rules
97%Prettier output compatibility

1. What Is Biome?

Biome is a unified toolchain for web projects, written entirely in Rust. It combines a formatter (replacing Prettier) and linter (replacing ESLint) into a single tool, with a single configuration file biome.json. Biome supports JavaScript, TypeScript, JSX, TSX, JSON, CSS, and GraphQL.

Why Rust Changes the Game

ESLint and Prettier are written in JavaScript — running on V8 single-threaded. Biome is written in Rust with parallel parsing, multi-threaded processing, and a zero-copy memory model. The result: linting a 10,000-line monorepo in ~200ms instead of 3-5 seconds.

2. Biome vs ESLint + Prettier — Detailed Comparison

CriteriaBiomeESLint + Prettier
Written inRustJavaScript
Lint speed (10k files)~0.8 seconds~45 seconds
Format speed (10k files)~0.3 seconds~12 seconds
Config files1 file (biome.json)4+ files (.eslintrc, .prettierrc, ignore files...)
Format/lint conflictsNever (same tool)Frequent (needs eslint-config-prettier)
Type-aware lintingYes (v2.0+)Yes (typescript-eslint)
Plugin systemGritQL plugins (v2.0+)JavaScript plugins (large ecosystem)
CSS lintingYes (built-in)Needs separate stylelint
LSP/IDE supportBuilt-in LSP serverSeparate extensions per tool
Lint rules count491+~300 core + plugins

3. Installing and Configuring Biome

3.1. Installation

# Install in your project
npm install --save-dev --save-exact @biomejs/biome

# Or use pnpm/bun
pnpm add -D @biomejs/biome
bun add -D @biomejs/biome

# Create default configuration
npx @biomejs/biome init

3.2. Basic biome.json Configuration

{
  "$schema": "https://biomejs.dev/schemas/2.4.0/schema.json",
  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 100
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "complexity": {
        "noExcessiveCognitiveComplexity": {
          "level": "warn",
          "options": { "maxAllowedComplexity": 15 }
        }
      },
      "suspicious": {
        "noExplicitAny": "error"
      },
      "style": {
        "useConst": "error",
        "noNonNullAssertion": "warn"
      }
    }
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single",
      "trailingCommas": "all",
      "semicolons": "always"
    }
  },
  "css": {
    "linter": { "enabled": true },
    "formatter": { "enabled": true }
  }
}

3.3. Running Biome

# Format all files
npx biome format --write .

# Lint with auto-fix
npx biome lint --write .

# Run both (check = lint + format)
npx biome check --write .

# CI mode — check only, no modifications
npx biome ci .

4. Biome's Internal Architecture

graph TD
    A["Source Files
JS/TS/JSX/CSS/JSON"] --> B["Biome Core
Rust Engine"] B --> C["Parser
Concrete Syntax Tree"] C --> D{"Parallel Processing"} D --> E["Formatter
Pretty-print CST"] D --> F["Linter
491+ Rules Analysis"] D --> G["Type Resolver
Type-aware rules"] E --> H["Formatted Output"] F --> I["Diagnostics
+ Auto-fix suggestions"] G --> F style B fill:#e94560,stroke:#fff,color:#fff style C fill:#2c3e50,stroke:#fff,color:#fff style D fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style E fill:#4CAF50,stroke:#fff,color:#fff style F fill:#16213e,stroke:#fff,color:#fff style G fill:#2c3e50,stroke:#fff,color:#fff

Biome's processing pipeline — parse once, lint + format in parallel

The key insight: Biome parses source code only once into a Concrete Syntax Tree (CST), then both the formatter and linter share this tree. ESLint + Prettier must parse separately — double parsing wastes significant time on large codebases.

5. New Features in Biome v2.x (2026)

5.1. Type-Aware Linting

Starting from v2.0, Biome supports lint rules based on type information. The first and most requested rule: noFloatingPromises.

// ❌ Biome will flag this — Promise not awaited
async function fetchData() {
  fetch('/api/users'); // noFloatingPromises: Promise returned but not awaited
}

// ✅ Correct
async function fetchData() {
  await fetch('/api/users');
}

5.2. Plugin System with GritQL

Biome v2.0 introduces a plugin system using GritQL — a query language for code patterns. You can write custom lint rules without writing Rust:

{
  "plugins": ["./biome-plugins/no-console-in-prod.grit"]
}
// no-console-in-prod.grit
`console.$method($args)` where {
  $method <: or { "log", "debug", "info" },
  // Allow console.error and console.warn
}

5.3. Linter Domains — Auto-Enable Rules by Dependencies

{
  "linter": {
    "domains": {
      "react": "error",
      "next": "warn",
      "solid": "off"
    }
  }
}

Biome automatically detects package.json dependencies and suggests enabling the corresponding domain. If your project uses React, all rules in the react domain (JSX accessibility, hooks rules, etc.) are automatically activated.

5.4. Embedded CSS + GraphQL in JavaScript

// Biome v2.4 automatically formats and lints CSS-in-JS
const Button = styled.button`
  background: #e94560;
  padding: 12px 24px;
  border: none;
  border-radius: 8px;
  color: #fff;
  cursor: pointer;

  &:hover {
    background: #c73854;
  }
`;

// GraphQL queries are also linted + formatted
const GET_USERS = gql`
  query GetUsers($limit: Int!) {
    users(limit: $limit) {
      id
      name
      email
    }
  }
`;

6. Migrating from ESLint + Prettier to Biome

6.1. Automatic Migration

# Biome reads your ESLint/Prettier config and converts it
npx @biomejs/biome migrate eslint --write
npx @biomejs/biome migrate prettier --write

The migrate eslint command reads your .eslintrc.* and maps corresponding rules to biome.json. Rules without equivalents are listed for your review.

6.2. Migration Strategy for Large Projects

graph LR
    A["Phase 1
Formatter only"] --> B["Phase 2
Lint recommended"] B --> C["Phase 3
Strict rules"] C --> D["Phase 4
Remove ESLint"] style A fill:#4CAF50,stroke:#fff,color:#fff style B fill:#2c3e50,stroke:#fff,color:#fff style C fill:#e94560,stroke:#fff,color:#fff style D fill:#16213e,stroke:#fff,color:#fff

4-phase migration roadmap for large projects

Phase 1 — Formatter only: Enable Biome formatter, disable Prettier. This is the safest step as it only changes formatting.

{
  "formatter": { "enabled": true },
  "linter": { "enabled": false }
}

Phase 2 — Lint recommended: Enable linter with recommended rules. Optionally keep ESLint for Vue SFC files (Biome doesn't fully support Vue SFC yet).

Phase 3 — Strict rules: Enable additional strict rules, address remaining violations.

Phase 4 — Remove ESLint: Completely remove ESLint + Prettier + config packages.

# Cleanup after migration
npm uninstall eslint prettier eslint-config-prettier \
  eslint-plugin-vue @typescript-eslint/eslint-plugin \
  @typescript-eslint/parser

# Remove old config files
rm .eslintrc.js .prettierrc .eslintignore .prettierignore

7. CI/CD and IDE Integration

7.1. GitHub Actions

name: Code Quality
on: [push, pull_request]

jobs:
  biome:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - name: Biome CI Check
        run: npx biome ci .

7.2. VS Code Extension

Biome provides an official VS Code extension with a built-in LSP server:

// .vscode/settings.json
{
  "editor.defaultFormatter": "biomejs.biome",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "quickfix.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  }
}

How Fast Is Format on Save?

Biome formats a file in ~1-2ms (Prettier takes ~50-100ms). With format-on-save, you'll never notice any delay — files are formatted instantly when you press Ctrl+S.

8. Biome with Vue.js Projects

Vue SFC Limitation

As of v2.4, Biome does not yet support parsing/linting/formatting the <template> and <style> sections in Vue Single File Components (.vue). Biome only processes the <script> and <script setup> sections. Full Vue SFC support is on the 2026 roadmap.

Practical strategy for Vue projects:

  • Use Biome for all .ts, .tsx, .json, .css files
  • Keep eslint-plugin-vue only for .vue files (template rules)
  • Disable Prettier entirely — Biome handles formatting for non-Vue files
{
  "files": {
    "ignore": ["**/*.vue"]
  },
  "formatter": { "enabled": true },
  "linter": { "enabled": true }
}

9. Real-World Monorepo Benchmarks

TaskESLint + PrettierBiomeSpeedup
Lint 5,000 TS files22.4s0.5s45x
Format 5,000 files6.8s0.2s34x
CI check (lint + format)31.2s0.6s52x
IDE feedback (single file)50-100ms1-2ms50x
npm install (deps)~45MB (8 packages)~12MB (1 package)73% smaller

10. Conclusion

Biome isn't just "faster ESLint + Prettier" — it represents a new generation of JavaScript tooling written in system-level languages. With 25-50x faster speed, a single configuration file, type-aware linting, GritQL plugin system, and built-in CSS/GraphQL support, Biome is gradually becoming the new standard for code quality in the JavaScript/TypeScript ecosystem.

If you're starting a new project — there's no reason not to use Biome. If you're maintaining a legacy project with ESLint + Prettier — the migration path is clear with biome migrate. Your CI pipeline will be faster, your DX will be smoother, and you'll have fewer config files scattered across your project root.

References