Tutorials

Biome vs ESLint vs Prettier: Replace Both with One Tool (2026)

Biome replaces ESLint and Prettier in one install, zero config, and runs 10-100x faster. Here's a complete migration guide with real benchmarks and when to stick with ESLint.

May 11, 202611 min read
Share:
Biome vs ESLint vs Prettier: Replace Both with One Tool (2026)

Your Next.js project probably has this setup: ESLint with 6 plugins, Prettier, .eslintrc.json, .prettierrc, .prettierignore, eslint-config-next, @typescript-eslint/eslint-plugin, @typescript-eslint/parser, and a lint-staged config that still randomly fails in CI.

Biome replaces all of it. One tool, one config file, zero plugins to install. And it runs in milliseconds instead of seconds.

This guide covers what Biome actually is, how it compares to ESLint and Prettier in practice, the full migration path, and the cases where you should still use ESLint.

What Is Biome?

Biome is a unified linter and formatter for JavaScript, TypeScript, JSX, TSX, JSON, and CSS — written in Rust. It started as Rome (a 2020 initiative to consolidate JS tooling) and was reborn as Biome in 2023 after the original project stalled.

What makes it different:

  • One binary, no plugins — lint + format from a single biome executable
  • Zero configuration — sensible defaults out of the box, one optional biome.json
  • Rust performance — formats a 50k line codebase in under 100ms
  • TypeScript-native — no separate parser to install, TypeScript is a first-class target
  • Stable API — the project is maintained and actively developed, v2 released in 2025

It is not a drop-in replacement for every ESLint configuration. If you rely on specific plugins (jest, testing-library, storybook, custom rules), read the "when to stay on ESLint" section first.

Performance: Real Numbers

These are consistent with what the Biome team publishes and what projects report in practice:

TaskESLint + PrettierBiome
Format 250 files~2.5s~50ms
Lint 250 files~4s~100ms
Format + lint~6s~120ms

The difference is most noticeable in --watch mode and lint-staged pre-commit hooks. Instead of waiting 3–6 seconds on every commit, hooks run in under 200ms.

Install Biome

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

Pin the exact version (--save-exact) — Biome follows semantic versioning but the config format can change between minors.

Initialize the config file:

npx @biomejs/biome init

This creates biome.json:

{
  "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
  "vcs": {
    "enabled": false,
    "clientKind": "git",
    "useIgnoreFile": false
  },
  "files": {
    "ignoreUnknown": false,
    "ignore": []
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space"
  },
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "double"
    }
  }
}

Add scripts to package.json:

{
  "scripts": {
    "lint": "biome lint --write .",
    "format": "biome format --write .",
    "check": "biome check --write ."
  }
}

biome check runs both lint and format together — this is the command you'll use most.

biome.json — Full Configuration

A production-ready config for a TypeScript/Next.js project:

{
  "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true
  },
  "files": {
    "ignore": [
      "node_modules",
      ".next",
      "dist",
      "build",
      "coverage",
      "*.min.js"
    ]
  },
  "formatter": {
    "enabled": true,
    "formatWithErrors": false,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineEnding": "lf",
    "lineWidth": 100,
    "attributePosition": "auto"
  },
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "correctness": {
        "noUnusedVariables": "error",
        "noUnusedImports": "error",
        "useExhaustiveDependencies": "warn"
      },
      "style": {
        "noNonNullAssertion": "warn",
        "useConst": "error",
        "useTemplate": "error"
      },
      "suspicious": {
        "noExplicitAny": "warn",
        "noConsoleLog": "warn"
      },
      "performance": {
        "noAccumulatingSpread": "error"
      },
      "a11y": {
        "useAltText": "error",
        "useButtonType": "error"
      }
    }
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single",
      "trailingCommas": "all",
      "semicolons": "asNeeded"
    }
  },
  "overrides": [
    {
      "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.test.tsx"],
      "linter": {
        "rules": {
          "suspicious": {
            "noExplicitAny": "off"
          }
        }
      }
    }
  ]
}

vcs.useIgnoreFile: true makes Biome respect your .gitignore automatically.

Migrating from ESLint + Prettier

Biome has a migration command that reads your existing ESLint config and maps rules to Biome equivalents:

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

This reads .eslintrc.* and .prettierrc.* and generates a biome.json with the closest equivalent rules. Rules that Biome doesn't support are listed in the output so you know what you're losing.

After migration:

  1. Remove ESLint and Prettier dependencies:
npm uninstall eslint prettier eslint-config-next @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-react eslint-plugin-react-hooks
  1. Delete config files:
rm .eslintrc.json .eslintignore .prettierrc .prettierignore
  1. Run Biome on your entire codebase to auto-fix:
npx @biomejs/biome check --write .
  1. Review the changes, commit, done.

VS Code Integration

Install the Biome extension: marketplace.visualstudio.com/items?itemName=biomejs.biome

Update .vscode/settings.json:

{
  "editor.defaultFormatter": "biomejs.biome",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  },
  "[javascript]": { "editor.defaultFormatter": "biomejs.biome" },
  "[typescript]": { "editor.defaultFormatter": "biomejs.biome" },
  "[typescriptreact]": { "editor.defaultFormatter": "biomejs.biome" },
  "[json]": { "editor.defaultFormatter": "biomejs.biome" }
}

Disable Prettier in VS Code for this project to avoid conflicts:

{
  "prettier.enable": false
}

CI Integration

# .github/workflows/ci.yml
- name: Biome check
  run: npx @biomejs/biome ci .

biome ci is like biome check but exits with a non-zero code on any issue and doesn't write changes — exactly what you want in CI.

Pre-commit Hooks with lint-staged

npm install --save-dev lint-staged husky
// package.json
{
  "lint-staged": {
    "*.{js,ts,jsx,tsx,json}": ["biome check --write --no-errors-on-unmatched"]
  }
}

This runs in ~100ms on staged files instead of 3–6 seconds with ESLint.

Biome vs ESLint: Rule Coverage

Biome's recommended rules cover the most common issues. Here's how the major categories map:

CategoryESLint equivalentBiome coverage
Unused vars/importsno-unused-vars, @typescript-eslint/no-unused-varsnoUnusedVariables, noUnusedImports
React hookseslint-plugin-react-hooksuseExhaustiveDependencies, useHookAtTopLevel
Accessibilityeslint-plugin-jsx-a11y✅ 20+ a11y rules
TypeScript@typescript-eslint/*✅ Most common rules
no-consoleno-consolenoConsoleLog
Import orderimport/orderorganizeImports
ComplexitycomplexitynoExcessiveCognitiveComplexity

Biome vs Prettier: Formatting Differences

Biome is compatible with Prettier for most cases, but there are some differences in output. Running biome migrate prettier handles most of them.

Key differences to be aware of:

  • Biome formats template literals differently in some edge cases
  • Biome's JSX formatting is slightly different for multi-attribute elements
  • Trailing commas behavior is configurable (same as Prettier's trailingComma)

The output is functionally identical in the vast majority of cases. If you have existing Prettier-formatted code, run biome format --write . and review the diff — it'll be minimal.

When to Stay on ESLint

Biome doesn't support plugins, so if your project depends on any of these, you'll need to keep ESLint (or run both):

  • eslint-plugin-jest — test-specific rules (no test.only, proper matchers)
  • eslint-plugin-testing-library — React Testing Library best practices
  • eslint-plugin-storybook — Storybook-specific rules
  • eslint-plugin-security — Security audit rules
  • Custom rules your team wrote
  • eslint-plugin-tailwindcss — Tailwind class ordering/validation

Biome's roadmap includes a plugin system, but it's not available yet in a stable form. Until then, for Jest-heavy codebases or teams with custom rules, ESLint remains the more complete solution.

You can run both in the same project — use Biome for formatting and basic linting, ESLint only for the specific plugins you need:

// package.json
{
  "scripts": {
    "lint": "biome check . && eslint . --ext .ts,.tsx"
  }
}

Biome in Monorepos

For Turborepo or Nx monorepos, you can have a root biome.json and override settings per package:

// packages/api/biome.json
{
  "extends": ["../../biome.json"],
  "linter": {
    "rules": {
      "suspicious": {
        "noConsoleLog": "off"
      }
    }
  }
}
# Run from root
biome check --write .
 
# Run for specific package
biome check --write packages/web

Disabling Rules Per Line

Same syntax as ESLint:

// biome-ignore lint/suspicious/noExplicitAny: third-party type is broken
function process(data: any) { ... }
 
// biome-ignore lint: entire line ignored
const x = eval(code)
 
// biome-ignore format: keep this formatted manually
const matrix = [
  1, 0, 0,
  0, 1, 0,
  0, 0, 1,
]

Should You Switch?

ScenarioRecommendation
New project, TypeScript + ReactUse Biome from day one
Existing project, mostly standard ESLintMigrate — the biome migrate command handles it
Existing project, heavy Jest/Testing Library rulesKeep ESLint for those, consider Biome for formatting
Monorepo with custom lint rulesEvaluate per package
Large team with established ESLint configBenchmark first, migrate gradually

For any new project in 2026, Biome is the default choice. The tooling is stable, the performance difference is real, and the zero-config experience is a genuine improvement over the ESLint plugin ecosystem.

For projects using Hono.js or Bun, Biome pairs especially well — the entire toolchain becomes Rust-native or Bun-native, and CI times drop dramatically.

#biome#eslint#prettier#typescript#tooling
Share:

Enjoyed this article?

Join 2,400+ developers getting weekly insights on Claude Code, React, and AI tools.

No spam. Unsubscribe anytime. By subscribing you agree to our Privacy Policy.