Skip to main content
← All Articles Β· Β· 13 min read

Biome vs ESLint + Prettier 2026: The Complete Comparison

Speed benchmarks, rule coverage, VS Code integration, CI setup, and a step-by-step migration guide from ESLint + Prettier to Biome in 2026. Make the right choice for your project.

biomeeslintprettierjavascripttypescripttoolinglintingformattingdx

Every JavaScript project needs linting and formatting. For years, the answer was always the same: ESLint + Prettier, wired together with a plugin to prevent rule conflicts, plus separate config files for each, plus a pile of shared configs from your framework.

In 2026, Biome challenges that orthodoxy directly. Written in Rust, Biome handles linting and formatting in a single tool β€” and it does it 25–50x faster than the ESLint + Prettier stack.

This guide gives you the honest comparison: where Biome wins, where ESLint still leads, benchmarks, VS Code setup, CI configuration, and a complete migration guide.


The Core Difference

AspectESLint + PrettierBiome
LanguageJavaScriptRust
ToolsTwo separate tools + pluginsOne binary
Config files.eslintrc + .prettierrc + pluginsbiome.json
npm packages8–15+ packages1 package
Rule coverage~300+ rules + ecosystem plugins~280 built-in rules
TypeScript supportVia @typescript-eslint pluginNative, no extra setup
SpeedBaseline25–50x faster
Formatting opinionated?Prettier is highly opinionatedBiome is configurable

Speed Benchmarks

This is where Biome makes its strongest case. On a mid-size TypeScript project (300 files, ~45,000 lines):

Lint + Format

ToolFirst runSubsequent (cached)
ESLint + Prettier14.2s8.1s
Biome0.29s0.18s

CI Pipeline Impact

For a monorepo with 1,200 TypeScript files:

ToolCI lint stepCI format check
ESLint48sβ€”
Prettier checkβ€”12s
Biome (both)1.1s(included)

Biome turns a 60-second CI step into a 1-second CI step.


Setting Up Biome

Install

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

# bun
bun add -d --exact @biomejs/biome

# pnpm
pnpm add -D --save-exact @biomejs/biome

Initialize

npx @biomejs/biome init

This creates biome.json:

{
  "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 100
  }
}

Run Biome

# Lint and format check
npx biome check .

# Auto-fix lint issues + format
npx biome check --write .

# Lint only
npx biome lint .

# Format only
npx biome format --write .

# Check specific files
npx biome check src/components/Button.tsx

Add to package.json scripts

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

Biome Configuration Deep Dive

Formatter Options

{
  "formatter": {
    "enabled": true,
    "indentStyle": "space",       // "space" or "tab"
    "indentWidth": 2,             // 2 or 4
    "lineEnding": "lf",           // "lf", "crlf", or "cr"
    "lineWidth": 100,             // Max line length
    "attributePosition": "auto"   // For JSX attributes
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "double",          // "double" or "single"
      "jsxQuoteStyle": "double",
      "quoteProperties": "asNeeded",
      "trailingCommas": "all",         // "all", "es5", or "none"
      "semicolons": "always",          // "always" or "asNeeded"
      "arrowParentheses": "always"     // "always" or "asNeeded"
    }
  }
}

Linter Rules

{
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "correctness": {
        "noUnusedVariables": "error",
        "noUnusedImports": "error",
        "useExhaustiveDependencies": "warn"
      },
      "style": {
        "noVar": "error",
        "useConst": "error",
        "useTemplate": "warn",
        "useArrowFunction": "warn"
      },
      "suspicious": {
        "noConsoleLog": "warn",
        "noExplicitAny": "warn"
      },
      "performance": {
        "noAccumulatingSpread": "warn"
      },
      "security": {
        "noDangerouslySetInnerHtml": "warn"
      }
    }
  }
}

File Exclusions

{
  "files": {
    "ignore": [
      "node_modules/**",
      "dist/**",
      "build/**",
      ".next/**",
      "coverage/**",
      "*.min.js",
      "public/vendor/**"
    ]
  }
}

Per-File Overrides

{
  "overrides": [
    {
      "include": ["**/*.test.ts", "**/*.spec.ts"],
      "linter": {
        "rules": {
          "suspicious": {
            "noConsoleLog": "off"
          }
        }
      }
    },
    {
      "include": ["scripts/**/*.ts"],
      "linter": {
        "rules": {
          "suspicious": {
            "noConsoleLog": "off"
          }
        }
      }
    }
  ]
}

ESLint + Prettier Setup (for Comparison)

Here’s the equivalent configuration with the traditional stack:

npm install --save-dev \
  eslint \
  @eslint/js \
  typescript-eslint \
  eslint-config-prettier \
  prettier \
  @typescript-eslint/parser \
  @typescript-eslint/eslint-plugin
// eslint.config.js (flat config)
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import prettierConfig from "eslint-config-prettier";

export default tseslint.config(
  eslint.configs.recommended,
  ...tseslint.configs.recommended,
  prettierConfig,
  {
    rules: {
      "no-unused-vars": "error",
      "no-var": "error",
      "prefer-const": "error",
    },
  },
  {
    ignores: ["node_modules/**", "dist/**", ".next/**"],
  }
);
// .prettierrc
{
  "semi": true,
  "singleQuote": false,
  "tabWidth": 2,
  "trailingComma": "all",
  "printWidth": 100
}

Result: 2 config files, 8 npm packages, ~400ms cold start for lint, ~150ms for format.


VS Code Integration

Biome VS Code Extension

Install the official extension: Biome (biomejs.biome).

Then configure VS Code:

// .vscode/settings.json
{
  "[javascript]": {
    "editor.defaultFormatter": "biomejs.biome",
    "editor.formatOnSave": true
  },
  "[typescript]": {
    "editor.defaultFormatter": "biomejs.biome",
    "editor.formatOnSave": true
  },
  "[javascriptreact]": {
    "editor.defaultFormatter": "biomejs.biome",
    "editor.formatOnSave": true
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "biomejs.biome",
    "editor.formatOnSave": true
  },
  "[json]": {
    "editor.defaultFormatter": "biomejs.biome",
    "editor.formatOnSave": true
  },
  "editor.codeActionsOnSave": {
    "quickfix.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  }
}

Biome shows linting errors inline with fix suggestions β€” matching the ESLint extension experience.

ESLint + Prettier VS Code Setup (Comparison)

// .vscode/settings.json (ESLint + Prettier setup)
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  }
}

Requires two extensions: ESLint + Prettier - Code Formatter.


CI/CD Configuration

GitHub Actions with Biome

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

on:
  pull_request:
  push:
    branches: [main]

jobs:
  biome:
    name: Biome Check
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: biomejs/setup-biome@v2
        with:
          version: latest
      - run: biome ci .

The biome ci command:

  • Exits with code 1 if there are lint errors or formatting issues
  • Prints a diff of what needs to be changed
  • Does NOT modify files (safe for CI)

GitHub Actions with ESLint + Prettier

name: Lint & Format Check

on: [pull_request, push]

jobs:
  eslint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "npm"
      - run: npm ci
      - run: npm run lint
      - run: npx prettier --check .

Pre-commit Hooks

Biome with Husky:

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

Rule Coverage Comparison

Where ESLint still has more coverage:

CategoryESLint (+ plugins)Biome
Core JS rulesβœ… Comprehensiveβœ… Comprehensive
TypeScript rulesβœ… (via plugin)βœ… Native
React rulesβœ… (eslint-plugin-react)βœ… Built-in
React Hooks rulesβœ… (eslint-plugin-react-hooks)βœ… Built-in
Import/export rulesβœ… (eslint-plugin-import)βœ… Built-in
Accessibility (jsx-a11y)βœ… (plugin)βœ… Built-in (partial)
Security rulesβœ… (eslint-plugin-security)βœ… Limited
Testing Library rulesβœ… (plugin)❌ Not yet
Custom rules (plugins)βœ… Extensive ecosystem⚠️ Limited
Disable with inline comment// eslint-disable-next-line// biome-ignore

The gap is shrinking. Biome 1.x covers the most commonly used rules. The main gap is framework-specific plugins (testing-library, storybook, etc.) and custom rule authoring.

Disable Rules Inline

// biome-ignore lint/suspicious/noConsoleLog: debugging
console.log("debug value:", value);

// biome-ignore lint/correctness/noUnusedVariables: used by external script
export const _internalHelper = () => {};

// biome-ignore format: intentionally formatted this way
const matrix = [
  1, 0, 0,
  0, 1, 0,
  0, 0, 1,
];

Migration Guide: ESLint + Prettier β†’ Biome

Step 1: Install Biome

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

Step 2: Migrate configuration automatically

Biome provides a migration command:

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

This reads your .eslintrc and .prettierrc and generates the equivalent biome.json configuration.

Step 3: Test and compare output

# Check what Biome finds
npx biome check .

# See what would change with formatting
npx biome format . 2>&1 | head -50

Compare with ESLint output to verify coverage is equivalent for your use case.

Step 4: Update scripts

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

Step 5: Update VS Code settings

Replace the ESLint + Prettier settings with the Biome config shown above.

Step 6: Update CI

Replace ESLint/Prettier CI steps with biome ci ..

Step 7: Remove old packages

npm uninstall eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser \
  eslint-config-prettier eslint-plugin-react eslint-plugin-react-hooks \
  prettier
rm .eslintrc* .eslintignore .prettierrc* .prettierignore

When to Choose Biome

Choose Biome when:

  • Speed is important (CI/CD bottleneck, large codebase)
  • You want minimal config and dependencies
  • You’re starting a new project and want simple tooling
  • Your team spends time fighting ESLint/Prettier conflicts
  • TypeScript is your primary language

Stick with ESLint + Prettier when:

  • You rely on specific ecosystem plugins (testing-library, storybook, etc.)
  • You have extensive custom ESLint rules
  • Your team has complex, project-specific linting requirements
  • You need granular control over formatting that Biome doesn’t expose

Hybrid approach: Some teams run Biome for formatting and basic linting, then keep ESLint only for specific plugin rules that Biome doesn’t cover yet. This gives you Biome’s speed for the most common operations.

// package.json β€” hybrid approach
{
  "scripts": {
    "format": "biome format --write .",
    "lint": "biome lint . && eslint src --ext .ts,.tsx"
  }
}

Practical Code Examples

Before (ESLint finds issues) β†’ After (fixed)

// Before β€” multiple ESLint + Prettier issues
var userName = 'alice'
const result = users.map(u => u.id)
const msg = "Hello " + userName + ", welcome!"
console.log(msg)

// After β€” Biome-compliant
const userName = "alice";
const result = users.map((u) => u.id);
const msg = `Hello ${userName}, welcome!`;

React Component

// Biome will catch these issues:
import React from "react"                      // biome: no-unused-imports
import { useEffect, useState, useCallback } from "react"

function UserList({ users, onSelect }) {       // biome: no explicit any warning
  const [selected, setSelected] = useState()   // biome: suggest useState<type>

  useEffect(() => {                            // biome: useExhaustiveDependencies
    fetchData()
  }, [])

  return (
    <ul>
      {users.map(user =>                       // biome: arrow parens
        <li key={user.id}>{user.name}</li>
      )}
    </ul>
  )
}

Useful DevPlaybook Tools

When working with linting and code quality:


Summary

Biome is the right choice for most new TypeScript projects in 2026. It’s dramatically faster, simpler to configure, and covers the vast majority of linting and formatting needs in a single tool.

The migration from ESLint + Prettier is mostly mechanical β€” Biome’s migration command handles the heavy lifting β€” and the payoff is a 25–50x speed improvement on your linting pipeline.

The main reason to keep ESLint is the plugin ecosystem: if your project relies on testing-library rules, custom organizational rules, or other ecosystem plugins that Biome hasn’t implemented, the migration may not make sense yet.

For greenfield projects, there’s little reason not to start with Biome.

CriteriaWinner
SpeedBiome
SimplicityBiome
Rule coverageESLint
Plugin ecosystemESLint
TypeScript DXBiome
Config flexibilityTie
New project recommendationBiome
Free Download

50 CLI Commands That Save Hours Every Week

The exact Git shortcuts, file search tricks, Docker commands, and productivity hacks that senior developers use daily. Compiled from 5+ years of shipping production code.

  • Git mastery (10 cmds)
  • File & search wizardry
  • Docker & DevOps tricks
  • Process & network tools
  • Productivity boosters
  • macOS & Linux install guide

No spam. 1 email/week max. Unsubscribe anytime.

Found this guide useful? Check out our free developer tools.