Skip to main content

Guide

Biome vs ESLint + Prettier: The Linter Wars 2026

Biome vs ESLint + Prettier compared on speed, rule coverage, migration effort, and when to switch. The Rust-powered all-in-one toolchain challenges the.

·PkgPulse Team·
0

Before Biome, linting and formatting a typical JavaScript project required 127 npm packages, 3 config files, and a lint-staged hook that took seconds to run. Biome replaces all of that with a single binary that runs in 50ms. The question isn't whether Biome is faster — it's 10-25x faster. The question is whether "10-25x faster" matters enough to migrate from the ESLint ecosystem you already know.

TL;DR

Biome is the right choice for new projects, or for projects where lint/format speed is noticeably impacting developer experience. ESLint + Prettier is still the right choice for projects that rely on framework-specific ESLint plugins (Next.js, React, TypeScript strict rules) that Biome doesn't yet fully replicate. Biome v2.x has closed the gap significantly — but not all the way.

Key Takeaways

  • Biome: formats 1K files in 50ms vs. Prettier's 1-2s; lints 10K files in 0.8s vs. ESLint's 45s (~25x overall)
  • @biomejs/biome: ~5.4M weekly npm downloads, rapidly growing
  • eslint: ~85M weekly downloads — still dominant by 15x
  • Biome ships as a single binary with zero npm dependencies (127 packages → 1)
  • Biome v2.4 (March 2026) has 450+ lint rules and type-aware linting (own synthesizer, ~85% typescript-eslint coverage)
  • Biome's formatter is 96-97% compatible with Prettier — verified against Prettier's own test suite
  • ESLint's plugin ecosystem (react-hooks, testing-library, next.js) has key gaps in Biome

What Biome Is

Biome is a Rust-powered toolchain for JavaScript/TypeScript that replaces multiple tools at once:

  • Formatter: Prettier replacement
  • Linter: ESLint replacement
  • Import organizer: perfectionist/eslint-plugin-import replacement
  • (Planned): Bundler, test runner, type checker

It was forked from Rome (abandoned) and has been maintained by an active community since 2023. The current stable version is v2.x (2026).

Installation

# ESLint + Prettier (current setup)
npm install -D eslint @eslint/js eslint-config-prettier prettier typescript-eslint
# + plugins: eslint-plugin-react, eslint-plugin-jsx-a11y, etc.
# = 127 packages in node_modules

# Biome (alternative)
npm install -D --save-exact @biomejs/biome
# = 1 package

Configuration Comparison

Biome Config

// biome.json
{
  "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true
  },
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "correctness": {
        "noUnusedVariables": "error",
        "noUnusedImports": "error"
      },
      "suspicious": {
        "noExplicitAny": "warn"
      },
      "style": {
        "useConst": "error",
        "noVar": "error"
      }
    }
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 100
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single",
      "trailingCommas": "es5",
      "semicolons": "always"
    }
  }
}

ESLint + Prettier Config (Flat Config)

// eslint.config.js
import js from '@eslint/js';
import ts from 'typescript-eslint';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import a11y from 'eslint-plugin-jsx-a11y';

export default ts.config(
  js.configs.recommended,
  ...ts.configs.strictTypeChecked,
  {
    plugins: {
      react,
      'react-hooks': reactHooks,
      'jsx-a11y': a11y,
    },
    rules: {
      'react-hooks/rules-of-hooks': 'error',
      'react-hooks/exhaustive-deps': 'warn',
      'jsx-a11y/anchor-is-valid': 'error',
      // ... 50 more rules
    },
  }
);

// .prettierrc
// { "semi": true, "singleQuote": true, "tabWidth": 2 }

Biome: 1 file. ESLint + Prettier: 2-3 files, 127 packages.

Speed Benchmarks

Linting (10,000 TypeScript files)

ToolTime
Biome0.8s
ESLint (no type-checking)45s
ESLint (with typescript-eslint type-aware)120s+

Formatting (10,000 files)

ToolTime
Biome0.3s
Prettier12.1s

Pre-commit Hook Impact (typical project, 50 staged files)

ToolTime
Biome~200ms
ESLint + Prettier3-8s

The pre-commit time difference is the one developers feel most directly. Waiting 5+ seconds for every commit is a real friction point.

Rule Coverage Comparison

What Biome Has (450+ rules in v2.4)

  • Core correctness rules (unused vars, typeof, no-debugger, etc.)
  • TypeScript-aware rules (no-any, consistent-type-imports)
  • Style rules (const, no-var, arrow functions)
  • Import organization
  • Security rules (no-dangerouslySetInnerHTML, eval)

What ESLint Has That Biome Lacks

CategoryESLint PluginsBiome Status
React Hooks ruleseslint-plugin-react-hooksPartial (exhaustive-deps missing)
Accessibilityeslint-plugin-jsx-a11yPartial (15 rules added in v2.4)
Next.js specificeslint-config-next❌ Not supported
Import sortingeslint-plugin-import✅ Built-in
Testing Libraryeslint-plugin-testing-library❌ Not supported
Jest/Vitesteslint-plugin-jest/vitest❌ Not supported
Tailwind class sortingeslint-plugin-tailwindcss❌ Not yet
Storybookeslint-plugin-storybook❌ Not supported
TypeScript type-aware@typescript-eslint/no-unsafe-*Partial (~85% coverage, own synthesizer)

The eslint-plugin-react-hooks exhaustive-deps rule and testing framework plugins are the most significant blockers for most teams.

Prettier Compatibility

Biome's formatter achieves 96%+ compatibility with Prettier's output for most codebases. The remaining 4% is mostly edge cases with complex template literals and certain JSX patterns.

# Check Prettier compatibility for your codebase
npx @biomejs/biome format --write src/
# Then: git diff --stat to see what changed

Migration Strategy

New Projects: Start with Biome

npx @biomejs/biome init
# Creates biome.json with recommended settings

# Replace package.json scripts
# Before:
"lint": "eslint . --ext .ts,.tsx",
"format": "prettier --write .",
"lint:fix": "eslint . --fix && prettier --write .",

# After:
"lint": "biome lint ./src",
"format": "biome format ./src --write",
"check": "biome check ./src",  # lint + format together

Existing Projects: Official Migration Commands

Biome ships built-in migration commands that auto-convert ~70% of ESLint rules:

# Auto-convert .eslintrc → biome.json lint rules
npx @biomejs/biome migrate eslint --write

# Auto-convert .prettierrc → biome.json formatter config
npx @biomejs/biome migrate prettier --write

These commands read your existing config files and generate equivalent biome.json settings. Rules without a Biome equivalent are flagged for manual review. Typical migration time: 30–90 minutes depending on codebase complexity. For the remaining ~30%, you'll decide: add it back as a standalone ESLint rule, use the hybrid approach below, or accept the coverage reduction.

Gradual Migration

Option 1: Run Biome alongside ESLint (transitional)

npm install -D @biomejs/biome

# biome.json — disable rules covered by ESLint plugins you're keeping
{
  "linter": {
    "rules": {
      "recommended": false,  // Start with nothing
      "correctness": { "noUnusedVariables": "error" }
    }
  },
  "formatter": { "enabled": true }  // Replace Prettier immediately
}

Option 2: Biome for formatting, ESLint for linting (common hybrid)

Many teams replace Prettier with Biome's formatter (easy, high benefit) while keeping ESLint for linting (retains plugin ecosystem):

# Remove prettier and eslint-config-prettier
npm uninstall prettier eslint-config-prettier

# Add biome for formatting only
npm install -D @biomejs/biome
// biome.json
{
  "linter": { "enabled": false },
  "formatter": { "enabled": true }
}
// eslint.config.js — no more prettier integration needed

This hybrid reduces complexity while keeping ESLint's plugin ecosystem.

VS Code Integration

Both have strong editor support:

// .vscode/settings.json — Biome
{
  "editor.defaultFormatter": "biomejs.biome",
  "editor.formatOnSave": true,
  "[javascript]": { "editor.defaultFormatter": "biomejs.biome" },
  "[typescript]": { "editor.defaultFormatter": "biomejs.biome" }
}

// .vscode/settings.json — ESLint + Prettier
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "eslint.validate": ["javascript", "typescript", "javascriptreact", "typescriptreact"]
}

CI/CD Performance Impact

For a monorepo with 50K files:

Tool StackCI lint + format time
ESLint + Prettier8-15 minutes
Biome30-60 seconds

This is where the speed advantage translates directly to developer velocity and CI costs.

Biome provides a dedicated biome ci command (distinct from biome check) that outputs GitHub-native PR annotations — lint errors appear inline on pull requests without extra tooling:

# .github/workflows/ci.yml
- uses: biomejs/setup-biome@v2
  # auto-detects version from package.json / lockfile
- run: biome ci
  # outputs GitHub annotations for inline PR comments

ESLint requires manual setup to achieve the same inline annotation behavior (reviewdog or similar). Biome's CI story is simpler out of the box.

Biome v2.4 also added --reporter=sarif for GitHub Advanced Security integration.

When to Choose Biome

Choose Biome if:

  • Starting a new project
  • Your pre-commit hooks are noticeably slow (>3 seconds)
  • CI lint time is a bottleneck
  • You're in a monorepo where ESLint runs multiple times
  • Your project doesn't rely on jsx-a11y or complex React plugin rules
  • You want fewer dependencies and simpler config

Keep ESLint + Prettier if:

  • eslint-plugin-react-hooks exhaustive-deps is enforced in your codebase (most significant blocker)
  • You rely on eslint-plugin-testing-library or eslint-plugin-jest/vitest
  • eslint-config-next catches Next.js-specific errors
  • You need @typescript-eslint/no-unsafe-* rules with compiler-accurate precision
  • Your team has complex custom ESLint rules
  • Primary framework is Vue or Svelte (Biome support still experimental)

Monorepo Configuration and Cross-Package Consistency

In monorepos, consistent linting and formatting across packages is both more important and more complex. Each package may have slightly different rules (a Next.js app needs eslint-config-next, a Node.js package doesn't), while the formatting style (indentation, quote style, line length) should be consistent across the entire repository.

Biome's monorepo support uses a hierarchical configuration model. A root biome.json defines the baseline formatting and linting rules for the entire repository. Individual packages can extend or override root settings with their own biome.json — the nearest biome.json to a file takes precedence for that file. Running biome check from the repository root applies rules to all files in the monorepo in a single pass. The Biome CLI binary handles all packages simultaneously, which is why Biome's monorepo CI time of 30-60 seconds dramatically underperforms ESLint's 8-15 minutes for 50K-file monorepos — ESLint requires spawning per-package processes while Biome parallelizes within a single process.

ESLint's flat config (eslint.config.js) introduced in ESLint 9 enables a similar hierarchical approach. A root eslint.config.js defines baseline rules, and package-specific configs extend it with additional rules or overrides. Tools like @eslint/eslintrc provide FlatCompat for migrating from the old .eslintrc format. The challenge is that ESLint still spawns a new process per invocation, so a Turborepo or Nx pipeline running eslint in each package sequentially is significantly slower than running it once at the root.

For teams standardizing on Biome in a monorepo, the VS Code extension is also workspace-aware — it uses the biome.json closest to the open file, producing correct per-package linting in the editor without any additional IDE configuration. This workspace-aware behavior requires Biome v1.7+ and the Biome VS Code extension v2.0+, which resolve the config file relative to the open file rather than the workspace root.

Editor Performance and Developer Ergonomics at Scale

The in-editor experience at scale — for projects with thousands of files and complex rule sets — differs meaningfully between Biome and ESLint. TypeScript-aware ESLint rules (@typescript-eslint/no-unsafe-return, @typescript-eslint/no-floating-promises) require the TypeScript language service to build a full type-checked program. In a 100-file TypeScript project, this typically takes 5-15 seconds on first activation and then uses incremental compilation for subsequent changes. In a 1000-file project, initial activation can take 30-60 seconds, and ESLint's language server may consume 500MB+ of RAM as it builds the complete type graph.

Biome's type-aware linting uses its own type synthesizer rather than the TypeScript compiler, which runs in the same Rust process as the other linting rules. The initial activation in large projects is measured in milliseconds rather than seconds. The tradeoff is coverage — Biome's synthesizer covers approximately 85% of the type-aware rules that @typescript-eslint covers, with the gap being in the most complex type inference scenarios. For everyday code quality enforcement (no unused variables, no implicit any, consistent type import style), Biome's coverage is sufficient. For the most strict TypeScript-eslint configurations (strictTypeChecked) used in libraries where API surface correctness is critical, the remaining 15% gap matters.

The 2026 Landscape

Biome has grown from ~500K to ~5.4M weekly downloads in roughly 18 months. ESLint isn't declining (it grew 65% in 2025 to ~85M weekly as the JS ecosystem expanded) — but Biome is capturing new projects while ESLint retains existing ones. The npm download gap is ~15x today vs. 100x+ in 2024.

The 2026 roadmap includes expanded type-aware rules, better Vue/Svelte support, and enhanced monorepo handling. The eslint-plugin-react-hooks gap is the primary remaining blocker; once exhaustive-deps is implemented, the migration case for React projects becomes much stronger.

The pragmatic 2026 choice: New projects → Biome. Existing projects → swap Prettier for Biome's formatter immediately (15-minute win), then evaluate full migration based on your ESLint plugin dependencies.

Compare @biomejs/biome vs eslint vs prettier download trends on PkgPulse.

Compare Biome and Eslint-prettier package health on PkgPulse.

See also: AVA vs Jest and Biome vs ESLint + Prettier: The Linter Wars 2026, Biome vs ESLint + Prettier.

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.