Skip to main content

Biome vs ESLint + Prettier: The Linter Wars 2026

·PkgPulse Team
0

TL;DR

Biome hasn't replaced ESLint + Prettier yet, but the gap is closing fast. Biome lints and formats 25x faster than ESLint + Prettier, ships with 300+ rules, and requires zero configuration. The blockers: no plugin ecosystem, missing a handful of ESLint rules popular codebases rely on, and no Prettier plugin support (SVGO, Tailwind class sorting). For greenfield projects: Biome is the better default. For existing projects with custom ESLint configs: migration cost is real.

Key Takeaways

  • Biome: 1.2M downloads/week growing fast, 25x faster, zero config, 300+ rules
  • ESLint: 30M downloads/week, 10,000+ plugins, de-facto standard
  • Prettier: 20M downloads/week, near-universal formatting standard
  • Biome rule coverage: ~80% of ESLint + 90% of Prettier's formatting
  • Migration blocker: No eslint-plugin-tailwindcss, eslint-plugin-import, custom rules
  • Verdict: Greenfield → Biome; existing complex config → stay on ESLint until Biome v2

Downloads

PackageWeekly DownloadsTrend
eslint~30M→ Stable
prettier~20M→ Stable
@biomejs/biome~1.2M↑ Fast growing
oxlint~400K↑ Fast growing

Performance Numbers

The most compelling argument for Biome is raw speed. On a 300K LOC TypeScript codebase, the difference is not marginal — it's roughly 25x. This matters in two concrete places: CI pipelines where every second costs money, and pre-commit hooks where slow tools get disabled.

CodebaseESLintPrettierTotalBiomeSpeedup
50 TS files (~5K LOC)3.1s0.8s3.9s0.18s~22x
300 TS files (~30K LOC)12.4s3.2s15.6s0.61s~26x
2,000 TS files (~300K LOC)44.8s14.7s59.5s2.4s~25x

The speed comes from Biome's Rust implementation. ESLint and Prettier are both Node.js tools with significant startup overhead and single-threaded execution. Biome runs natively compiled code with multi-threaded file processing. There is no configuration that closes this performance gap — it is architectural.

# Benchmark your own project
time npx eslint . && time npx prettier --check .
time npx @biomejs/biome check .

For most teams running lint on pre-commit hooks, the difference between 3s and 0.2s is the difference between a hook that runs every commit and one that gets added to .husky/.gitignore after a week.


Zero Config Setup

Biome's setup is genuinely minimal. The contrast with ESLint + Prettier is stark, especially for teams who have inherited a legacy config with 12 plugins and a .eslintignore file that nobody understands.

# Biome setup — 2 commands, 1 config file
npm install --save-dev --save-exact @biomejs/biome
npx @biomejs/biome init
# Result: biome.json (one file)
# ESLint + Prettier setup — typical TypeScript + React project
npm install --save-dev \
  eslint prettier \
  eslint-config-prettier \
  @typescript-eslint/parser \
  @typescript-eslint/eslint-plugin \
  eslint-plugin-react \
  eslint-plugin-react-hooks \
  eslint-plugin-import \
  eslint-plugin-tailwindcss \
  prettier-plugin-tailwindcss
# Result: eslint.config.js + .prettierrc + .eslintignore (3+ files, 10 packages)

The biome.json created by npx @biomejs/biome init is immediately usable. Every rule is either enabled by default (with sensible settings) or explicitly disabled. There is no implicit configuration to inherit and no peer dependency version dance.


Biome: The All-In-One Tool

npm install --save-dev --save-exact @biomejs/biome
npx @biomejs/biome init
// biome.json — zero-config to start:
{
  "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
  "organizeImports": { "enabled": true },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "correctness": { "noUnusedVariables": "warn" },
      "style": { "useConst": "error" },
      "suspicious": { "noExplicitAny": "warn" }
    }
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 100
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single",
      "trailingCommas": "es5",
      "semicolons": "always"
    }
  }
}
# Run both lint + format check together:
npx @biomejs/biome check --apply .

# Format only:
npx @biomejs/biome format --write .

# Lint only:
npx @biomejs/biome lint --apply .

ESLint Flat Config

ESLint 9.x introduced flat config (eslint.config.js), which addresses one of ESLint's most persistent pain points: configuration inheritance and override complexity. The old .eslintrc.json format allowed cascading configs from parent directories, which created surprising behavior in monorepos. Flat config is explicit — you import what you use.

// OLD — .eslintrc.json (deprecated in ESLint 9)
{
  "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint"],
  "rules": {
    "no-console": "warn"
  }
}
// NEW — eslint.config.js (ESLint 9 flat config)
import js from '@eslint/js';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import reactPlugin from 'eslint-plugin-react';
import reactHooksPlugin from 'eslint-plugin-react-hooks';
import importPlugin from 'eslint-plugin-import';
import tailwindPlugin from 'eslint-plugin-tailwindcss';
import prettierConfig from 'eslint-config-prettier';

export default [
  js.configs.recommended,
  {
    files: ['**/*.{ts,tsx}'],
    languageOptions: {
      parser: tsParser,
      parserOptions: {
        project: './tsconfig.json',
      },
    },
    plugins: {
      '@typescript-eslint': tsPlugin,
      react: reactPlugin,
      'react-hooks': reactHooksPlugin,
      import: importPlugin,
      tailwindcss: tailwindPlugin,
    },
    rules: {
      ...tsPlugin.configs.recommended.rules,
      ...reactHooksPlugin.configs.recommended.rules,
      'tailwindcss/classnames-order': 'warn',
      'import/order': ['error', { 'newlines-between': 'always' }],
      '@typescript-eslint/no-explicit-any': 'warn',
    },
  },
  prettierConfig,  // Must be last
];

Flat config is better than what came before, but it is still more complex than Biome's single JSON file. The comparison: one biome.json vs eslint.config.js + .prettierrc + tsconfig.json references. ESLint flat config removed the hidden complexity; it did not eliminate the configuration volume.


Rule Coverage Gaps

Biome covers a substantial portion of the ESLint + Prettier rule surface, but the gaps in 2026 are real and affect specific categories of projects.

CategoryBiomeESLint ecosystem
TypeScript rules~200 rules200+ (typescript-eslint)
React hooksPartialFull (plugin-react-hooks)
Import orderingBasic organize-importsFull (plugin-import)
Tailwind class sorting✅ (prettier-plugin-tailwindcss)
Accessibility (jsx-a11y)
Custom rules✅ (unlimited plugins)
Unicorn rulesPartial✅ (eslint-plugin-unicorn)
Security rules✅ (eslint-plugin-security)

The most commonly cited gaps are TypeScript-aware rules (@typescript-eslint/no-unsafe-*, @typescript-eslint/no-floating-promises), framework-specific rules (eslint-plugin-react-hooks, eslint-plugin-next), Tailwind CSS class ordering, and accessibility rules. The Biome team is actively addressing these — the upcoming Biome v2 release is expected to add plugin support, which would allow the community to fill most of these gaps. But as of early 2026, if you rely on eslint-plugin-tailwindcss for class sorting or jsx-a11y for accessibility enforcement, Biome cannot replace your current setup.


Migration Strategy

The recommended path for teams with existing ESLint + Prettier configurations is not a hard cutover but a gradual replacement.

# Step 1: Check what Biome can migrate automatically
npx @biomejs/biome migrate eslint --write

# This reads your .eslintrc and generates biome.json equivalent
# Output shows which rules map and which don't
Migration output example:
  ✅ 47 rules migrated
  ⚠️  8 rules have no Biome equivalent:
    - tailwindcss/classnames-order → Use Prettier plugin instead
    - import/order → Biome has organize-imports (different behavior)
    - jsx-a11y/* → Not yet supported in Biome
    - custom-plugin/my-rule → Custom rules not supported

The practical hybrid approach: use Biome to handle formatting (replace Prettier entirely) and the TypeScript/React rules that Biome covers, while keeping a minimal ESLint config only for the rules Biome cannot handle yet.

// package.json — hybrid lint setup
{
  "scripts": {
    "lint": "biome check . && eslint --max-warnings 0 src/",
    "format": "biome format --write ."
  }
}
// Minimal eslint.config.js — only what Biome can't do yet
import tailwindPlugin from 'eslint-plugin-tailwindcss';
import a11y from 'eslint-plugin-jsx-a11y';

export default [
  { plugins: { tailwindcss: tailwindPlugin, 'jsx-a11y': a11y } },
  {
    rules: {
      'tailwindcss/classnames-order': 'warn',
      ...a11y.configs.recommended.rules,
    }
  },
];

This hybrid gives you Biome's 25x speed improvement for the bulk of your lint and format work, while ESLint handles only the narrow set of rules Biome does not yet cover. As Biome v2 matures, you progressively remove ESLint rules rather than doing a big-bang migration.


Editor Integration

Both Biome and ESLint have VS Code extensions, though their behavior differs in meaningful ways. The ESLint VS Code extension has been around since 2015 and is one of the most-installed extensions in the marketplace. It surfaces lint errors inline as you type and supports quick-fix actions for auto-fixable rules. Prettier's extension handles format-on-save. Most developers have both installed and configured as separate tools.

Biome's VS Code extension combines both functions in one extension. It surfaces lint errors and applies formatting through the same extension. The extension uses the Biome binary for analysis, which means it inherits Biome's speed advantage — hovering over an error or triggering format-on-save is noticeably faster than the equivalent ESLint + Prettier flow on large files.

// .vscode/settings.json — Biome as default formatter
{
  "[javascript][typescript][json]": {
    "editor.defaultFormatter": "biomejs.biome",
    "editor.formatOnSave": true
  },
  "editor.codeActionsOnSave": {
    "quickfix.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  }
}

For teams using the hybrid approach, the VS Code configuration becomes slightly more complex — you need to ensure Biome handles formatting while ESLint's extension handles the gap rules without conflicting. This is manageable but adds a setup step that new team members need to configure correctly.

Biome v2 and the Plugin System

Biome v2 is the most anticipated development in the Biome ecosystem. The primary addition is a plugin system that allows the community to write custom rules in GritQL (a structural code pattern language) or through a JavaScript plugin API. If the plugin system ships with the expected capability, it would address the primary remaining reason to keep ESLint in a hybrid setup — custom org-specific rules and third-party plugins for Tailwind, accessibility, and security.

The Biome team has been transparent about the development in their roadmap. As of early 2026, Biome v2 is in active development with an expected release date in mid-2026. Teams evaluating Biome today should factor in whether their remaining ESLint rule dependencies are likely to have Biome equivalents within their planning horizon.

Package Health

PackageWeekly DownloadsMaintainerStatus
@biomejs/biome~1.2MEmanuele Stoppa + communityActive, v2 in development
eslint~30MOpenJS FoundationActive, v9 stable
prettier~20MPrettier teamActive, v3 stable

Biome's growth from zero to 1.2M weekly downloads in two years reflects genuine adoption. The project is funded through Open Collective and the core team is active. ESLint and Prettier are deeply embedded in the JavaScript toolchain — their download numbers reflect transitive dependencies from every major framework scaffold. Neither is going away.


When to Choose

Choose Biome when:

  • Starting a new project with no legacy ESLint configuration
  • CI speed and pre-commit hook performance matter
  • You don't need Tailwind class sorting, jsx-a11y, or custom rules
  • The team prefers minimal configuration and zero-config defaults
  • You want to reduce the number of devDependencies significantly

Choose ESLint + Prettier when:

  • You need eslint-plugin-tailwindcss for Tailwind class ordering
  • Accessibility rules via jsx-a11y are required
  • You rely on custom ESLint rules or internal plugins
  • Migrating a large codebase with thousands of eslint-disable comments
  • Framework-specific rules (eslint-plugin-next, eslint-plugin-react-hooks) are critical

Consider Hybrid when:

  • You want Biome's speed benefits today without abandoning your rule coverage
  • You're willing to maintain a thin ESLint layer for gap rules
  • You want to progressively migrate as Biome v2 closes remaining gaps

For side-by-side download stats and trend data, see the Biome vs ESLint comparison on PkgPulse. If you're evaluating Rust-based linters more broadly, the oxlint vs ESLint performance comparison covers that angle in depth. Track Biome's weekly adoption growth on the ESLint package page alongside its competitors.

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.