<!-- PkgPulse AI-readable guide source -->
<!-- Canonical: https://www.pkgpulse.com/guides/how-to-migrate-eslint-to-biome -->
<!-- Raw Markdown: https://www.pkgpulse.com/guides/how-to-migrate-eslint-to-biome/raw.md -->
<!-- Source path: content/guides/how-to-migrate-eslint-to-biome.mdx -->

---
og_image: "/images/guides/how-to-migrate-eslint-to-biome.webp"
title: "How to Migrate from ESLint to Biome 2026"
description: "Step-by-step ESLint to Biome migration guide. Config conversion, rule mapping, CI setup, and handling the 95% of ESLint rules that Biome covers — plus what."
date: "2026-03-08"
author: "PkgPulse Team"
tags: ["biome", "eslint", "linting", "migration", "javascript", "2026"]
featured_comparison: "biome-vs-eslint"
tier: 1
---

## TL;DR

**Biome replaces ESLint + Prettier in a single tool and runs 25x faster.** Biome covers ~200+ ESLint rules natively — enough for most projects. Migration is 30-60 minutes: install Biome, run `biome migrate eslint`, review the output, delete `.eslintrc`. The main limitation: Biome doesn't support custom plugins or rules from the ecosystem (eslint-plugin-react-hooks still needed for certain rules). Most teams run Biome for the 95% and keep a minimal ESLint config for the remaining plugins.

## Key Takeaways

- **25x faster than ESLint** — Rust-based, processes files in parallel
- **Replaces both ESLint + Prettier** — one tool for linting and formatting
- **~200+ rules built-in** — covers `recommended` configs for most plugins
- **No plugin system yet** — third-party plugins not supported
- **`biome migrate eslint`** — official command that auto-converts your config

---

## Before You Start

Before installing Biome, take stock of what your current ESLint setup includes. This assessment takes five minutes and prevents surprises.

**Check your ESLint version and plugins:**

```bash
# Check ESLint version
npx eslint --version

# List what's installed
cat package.json | grep -E "eslint|prettier"
```

A typical TypeScript + React project has a `package.json` that looks like this in the dev dependencies section:

```json
{
  "devDependencies": {
    "eslint": "^8.57.0",
    "@typescript-eslint/eslint-plugin": "^7.0.0",
    "@typescript-eslint/parser": "^7.0.0",
    "eslint-plugin-react": "^7.34.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-jsx-a11y": "^6.8.0",
    "eslint-plugin-import": "^2.29.0",
    "eslint-config-prettier": "^9.1.0",
    "prettier": "^3.2.0"
  }
}
```

Go through each plugin and ask: "Does Biome cover this?" Here is the answer for the most common ones:

- **`@typescript-eslint`** — fully covered. Biome implements the core TypeScript rules natively.
- **`eslint-plugin-react`** — mostly covered. Biome has React-specific rules for common anti-patterns.
- **`eslint-plugin-react-hooks`** — partially covered. `exhaustive-deps` is covered via `correctness/useExhaustiveDependencies`. `rules-of-hooks` has a Biome equivalent but it is less comprehensive.
- **`eslint-plugin-jsx-a11y`** — well covered. Biome's `a11y` rule group covers the most important accessibility rules.
- **`eslint-plugin-import`** — partially covered. Import ordering is handled by `organizeImports`. Circular dependency detection (`no-cycle`) has no Biome equivalent.
- **`prettier`** — fully replaced. Biome's formatter handles all of Prettier's formatting with near-identical output.
- **Custom rules** — not covered. If your team has built custom ESLint rules, those have no equivalent in Biome. Keep ESLint for those.

The practical result for most projects: Biome covers everything except `react-hooks/rules-of-hooks` and `import/no-cycle`. You can run a minimal ESLint config for those two rules alongside Biome.

---

## Step 1: Install Biome

Install Biome as a dev dependency. The `--save-exact` flag is important — Biome's config schema is version-pinned and upgrades can change formatting slightly, which you want to be intentional about.

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

# Initialize the config file
npx @biomejs/biome init

# This creates biome.json at the project root
```

The `init` command produces a minimal `biome.json` that looks like this:

```json
{
  "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  }
}
```

This is your starting point. The `recommended: true` flag enables a curated set of around 100 rules that cover the most important safety and correctness checks. You will expand this in the next step.

---

## Step 2: Auto-Migrate Your ESLint Config

Biome ships an official migration command that reads your existing `.eslintrc` or `eslint.config.js` and maps known rules to their Biome equivalents:

```bash
# Auto-convert your ESLint config to Biome
npx @biomejs/biome migrate eslint --write

# This command:
# 1. Reads your .eslintrc / eslint.config.js
# 2. Maps rules to Biome equivalents
# 3. Updates biome.json with matched rules
# 4. Reports rules it couldn't migrate

# Example output:
# ✔ Migrated 45 rules
# ℹ 3 rules have no Biome equivalent:
#   - react-hooks/rules-of-hooks (use eslint-plugin-react-hooks)
#   - import/no-cycle (no equivalent)
#   - custom-rules/my-rule (custom plugin)
```

The `--write` flag applies the changes directly to `biome.json`. Without it, the command runs in dry-run mode and only reports what it would do.

After running, review the generated `biome.json`. It will be populated with the rules that Biome mapped automatically:

```json
{
  "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "a11y": {
        "useAltText": "error",
        "noAutofocus": "warn"
      },
      "complexity": {
        "noExtraBooleanCast": "error",
        "noMultipleSpacesInRegularExpressionLiteral": "error"
      },
      "correctness": {
        "noUnusedVariables": "error",
        "useExhaustiveDependencies": "warn"
      },
      "security": {
        "noDangerouslySetInnerHtml": "warn"
      },
      "style": {
        "noVar": "error",
        "useConst": "error",
        "useSingleVarDeclarator": "error"
      },
      "suspicious": {
        "noConsoleLog": "warn",
        "noDebugger": "error",
        "noDoubleEquals": "error"
      }
    }
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 80
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single",
      "trailingCommas": "es5",
      "semicolons": "always"
    }
  },
  "files": {
    "ignore": [
      "dist/**",
      "build/**",
      "node_modules/**",
      "*.min.js"
    ]
  }
}
```

---

## Step 3: Run Biome on Your Codebase

Run the linter against your entire codebase and compare the output to your ESLint output:

```bash
# Check for lint and formatting issues (no auto-fix)
npx biome check .

# Auto-fix safe issues
npx biome check --apply .

# Apply unsafe fixes (changes behavior — review carefully)
npx biome check --apply-unsafe .
```

On the first run, you will likely see a large number of formatting differences. This is expected — Biome's formatter and Prettier produce near-identical output but with minor differences in some edge cases. Run `biome format --write .` to apply formatting once across the entire codebase, commit that as a standalone formatting commit, then proceed. This keeps the formatting change separate from linting changes in your git history.

```bash
# Apply Biome formatting to all files in one pass
npx biome format --write .

# Commit the formatting change separately
git add -A
git commit -m "chore: apply Biome formatting"

# Then continue with the rest of the migration
```

The ESLint rule → Biome rule mapping reference:

| ESLint Rule | Biome Equivalent |
|-------------|-----------------|
| `no-unused-vars` | `correctness/noUnusedVariables` |
| `no-var` | `style/noVar` |
| `prefer-const` | `style/useConst` |
| `no-console` | `suspicious/noConsoleLog` |
| `no-debugger` | `suspicious/noDebugger` |
| `eqeqeq` | `suspicious/noDoubleEquals` |
| `no-extra-boolean-cast` | `complexity/noExtraBooleanCast` |
| `react-hooks/exhaustive-deps` | `correctness/useExhaustiveDependencies` |
| `jsx-a11y/alt-text` | `a11y/useAltText` |
| `jsx-a11y/no-autofocus` | `a11y/noAutofocus` |
| `no-danger` | `security/noDangerouslySetInnerHtml` |

---

## Step 4: Replace Prettier

Biome's formatter replaces Prettier completely. If you have a `.prettierrc` or `prettier.config.js`, map its settings to `biome.json` formatter options and then delete the Prettier config and package.

Common Prettier options and their Biome equivalents:

```json
// .prettierrc (before):
{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "printWidth": 80,
  "arrowParens": "always"
}

// biome.json formatter section (after):
{
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 80
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single",
      "trailingCommas": "es5",
      "semicolons": "always",
      "arrowParentheses": "always"
    }
  }
}
```

After updating `biome.json`, uninstall Prettier:

```bash
npm uninstall prettier eslint-config-prettier eslint-plugin-prettier

# Remove Prettier config files
rm .prettierrc .prettierignore prettier.config.js 2>/dev/null || true
```

Verify formatting still works:

```bash
npx biome format --write src/
```

---

## Step 5: Update CI/CD and Git Hooks

Replace `eslint` and `prettier` invocations in your CI pipeline and git hooks with Biome equivalents.

**Update `package.json` scripts:**

```json
{
  "scripts": {
    "lint": "biome lint .",
    "lint:fix": "biome lint --apply .",
    "format": "biome format --write .",
    "format:check": "biome format .",
    "check": "biome check --apply .",
    "ci": "biome ci ."
  }
}
```

The `biome ci` command is designed for CI environments. It runs lint and format checks with no `--apply` flag (fails on any issue instead of fixing it) and exits with a non-zero code if anything fails.

**Update lint-staged (if you use husky):**

```json
// package.json lint-staged config
{
  "lint-staged": {
    "*.{js,ts,jsx,tsx,json}": [
      "biome check --apply --no-errors-on-unmatched"
    ]
  }
}
```

**GitHub Actions CI workflow:**

```yaml
# .github/workflows/lint.yml
name: Lint & Format

on: [push, pull_request]

jobs:
  biome:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Biome
        uses: biomejs/setup-biome@v2
        with:
          version: latest

      - name: Run Biome CI
        run: biome ci .
        # biome ci exits non-zero on any lint or formatting issue
```

The `biomejs/setup-biome@v2` GitHub Action is the official action. It downloads the Biome binary without requiring a full Node.js install, which makes it faster than `npm ci` + `npx biome`.

---

## Step 6: Handle the Remaining ESLint Rules

For rules that Biome does not cover, keep a minimal ESLint configuration. The goal is to shrink your ESLint config to only the rules that have no Biome equivalent. Biome handles everything else.

```bash
# Keep only the plugins with no Biome equivalent
# Remove everything else
npm uninstall @typescript-eslint/eslint-plugin @typescript-eslint/parser \
  eslint-plugin-react eslint-plugin-jsx-a11y

# Keep these (they have no Biome equivalent):
# eslint-plugin-react-hooks — for rules-of-hooks
# eslint-plugin-import — for no-cycle (if you use it)
```

```json
// .eslintrc.json — minimal config for rules Biome can't handle
{
  "root": true,
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/rules-of-hooks": "error"
  },
  "ignorePatterns": ["dist/", "build/", "node_modules/"]
}
```

If you do not need `import/no-cycle` (most projects don't), you can potentially remove ESLint entirely. The `react-hooks/rules-of-hooks` rule is the last one most React projects need from ESLint after switching to Biome.

Run both in CI if you keep a minimal ESLint:

```bash
# CI script: run Biome first (fast), then minimal ESLint
biome ci . && eslint src/ --max-warnings 0
```

---

## Editor Integration

Install the official VS Code extension for format-on-save and inline diagnostic display.

VS Code extension ID: `biomejs.biome`

Install it from the command line:

```bash
code --install-extension biomejs.biome
```

Configure VS Code to use Biome as the default formatter and disable the ESLint and Prettier extensions for this project:

```json
// .vscode/settings.json
{
  "editor.defaultFormatter": "biomejs.biome",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "quickfix.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  },
  // Per-language formatter overrides
  "[javascript]": { "editor.defaultFormatter": "biomejs.biome" },
  "[typescript]": { "editor.defaultFormatter": "biomejs.biome" },
  "[typescriptreact]": { "editor.defaultFormatter": "biomejs.biome" },
  "[javascriptreact]": { "editor.defaultFormatter": "biomejs.biome" },
  "[json]": { "editor.defaultFormatter": "biomejs.biome" },
  "[jsonc]": { "editor.defaultFormatter": "biomejs.biome" },

  // Disable conflicting extensions
  "prettier.enable": false,
  "eslint.enable": false  // Only if you removed ESLint entirely
}
```

Commit this `.vscode/settings.json` to your repository so the whole team gets the same editor behavior automatically.

---

## Why Teams Switch to Biome

Beyond raw speed, there are several practical reasons teams migrate from ESLint + Prettier to Biome that are worth understanding before you commit to the migration.

**Configuration fatigue.** A typical ESLint setup for a TypeScript React project involves `eslint`, `@typescript-eslint/parser`, `@typescript-eslint/eslint-plugin`, `eslint-plugin-react`, `eslint-plugin-react-hooks`, `eslint-plugin-jsx-a11y`, `eslint-config-prettier`, and Prettier itself — eight packages, multiple config files, and a fragile dependency chain where version mismatches cause cryptic errors. Biome replaces all of this with one package and one config file.

**Consistency between formatting and linting.** ESLint and Prettier can conflict. You need `eslint-config-prettier` to disable ESLint's formatting rules so they don't fight Prettier. When you add a new ESLint rule or upgrade either tool, you can reintroduce conflicts. Biome owns both the formatter and the linter, so they are always consistent by design.

**Onboarding new developers.** When a new developer joins a project, setting up ESLint + Prettier in VS Code requires installing multiple extensions, potentially editing workspace settings to disable conflicting formatters, and debugging "why isn't format-on-save working." Biome is one extension, one config file, and it works immediately.

**Pre-commit hook performance at scale.** As a codebase grows, ESLint's pre-commit hook grows slower. Teams start adding `--cache` flags and optimizing `lint-staged` configurations to avoid linting unchanged files. Biome's 0.3-second cold lint time means you never need cache optimization — it stays fast regardless of project size.

The migration is not appropriate for every team. If you have heavy investment in custom ESLint rules, or if your tooling depends on ESLint's plugin ecosystem (some code codemods and refactoring tools hook into ESLint), evaluate carefully whether the gains outweigh the migration cost. For teams starting new projects or with standard ESLint setups, Biome is worth adopting from day one.

---

## Performance Comparison

The speed difference between ESLint and Biome is significant enough to change how you integrate linting into your workflow:

```bash
# Real benchmark: 500 TypeScript files

# ESLint (JavaScript):
# Cold lint: 8.2 seconds
# With cache: 2.1 seconds

# Biome (Rust):
# Cold lint: 0.3 seconds  — 27x faster cold
# Format: 0.1 seconds
# Always cold (no cache needed — it is fast enough)
```

The practical impact: on a pre-commit hook, ESLint with cache takes 2-3 seconds. Biome takes under a second. This is the difference between a hook that developers work around and one they keep enabled. Fast tooling gets used consistently.

---

## Biome in Monorepos

Biome works particularly well in monorepos because it supports per-package configuration through `biome.json` files at different directory levels. The root `biome.json` defines the baseline, and individual packages can extend or override settings.

```json
// packages/ui/biome.json — override root config for this package
{
  "extends": ["../../biome.json"],
  "linter": {
    "rules": {
      "a11y": {
        "useAltText": "error"  // Stricter accessibility rules for UI package
      }
    }
  }
}
```

For Turborepo monorepos, add a `biome` task to `turbo.json`:

```json
// turbo.json
{
  "tasks": {
    "lint": {
      "inputs": ["src/**/*.{ts,tsx}", "biome.json"]
    }
  }
}
```

Biome's fast execution means it does not need Turborepo's cache as much as ESLint does, but caching still helps for very large monorepos with many packages.

---

## Incremental Migration Strategy

If your codebase is large and you cannot fix all Biome warnings in one session, use Biome's suppression comments to temporarily ignore issues while you migrate incrementally:

```typescript
// Suppress a specific rule for one line
// biome-ignore lint/suspicious/noConsoleLog: debug in progress
console.log('investigating issue #1234');

// Suppress all rules for a block
// biome-ignore lint: legacy code, refactor pending
const x = eval(userInput);
```

You can also use the `overrides` section in `biome.json` to apply different rules to legacy directories:

```json
// biome.json — relax rules for legacy code
{
  "overrides": [
    {
      "include": ["src/legacy/**"],
      "linter": {
        "rules": {
          "suspicious": {
            "noConsoleLog": "off"
          }
        }
      }
    }
  ]
}
```

This lets you run `biome check .` in CI without being blocked by pre-existing issues in legacy code, while still enforcing Biome fully on new code. Over time, clean up the legacy directory and remove the override.

---

## Summary

The ESLint to Biome migration follows a predictable path: install, auto-migrate, handle the gaps, update CI. For most React + TypeScript projects with standard ESLint configs, the migration takes under an hour and the ongoing developer experience improvement is substantial. The 25x speed improvement is real and noticeable in daily use.

The one genuine limitation remains the plugin ecosystem. If your team has custom ESLint rules or relies heavily on plugins that have no Biome equivalent, plan to run both tools together rather than doing a full replacement. The two tools co-exist cleanly — Biome handles the bulk of the work at Rust speed, and a minimal ESLint handles the edge cases.

---

## Further Reading

- [Biome vs ESLint + Prettier — the linter wars in 2026](/guides/biome-vs-eslint-vs-oxlint-2026)
- [ESLint package health, download trends, and changelog](/packages/eslint)
- [Oxlint vs ESLint — Rust-based linting performance compared](/guides/oxlint-vs-eslint-rust-linting-performance-2026)
