Biome

23. August, 2025 11 min read Develop

One Tool to Replace ESLint and Prettier

For years, the standard frontend tooling setup meant installing ESLint for linting and Prettier for formatting, then spending time making them play nicely together. Biome changes that equation entirely — it's a single, Rust-powered tool that handles formatting, linting, and import sorting with remarkable speed.

With Biome v2 released in June 2025, the project has matured into a serious alternative to the ESLint + Prettier combination. In this post, we’ll explore what Biome offers, how to set it up, how to migrate from your existing tools, and whether it’s ready for your projects.

A Brief History

Biome’s story begins with Rome, an ambitious project created by Sebastian McKenzie — the creator of Babel and Yarn. Rome aimed to be an all-in-one toolchain for JavaScript development, replacing dozens of separate tools with a single unified solution.

Originally written in TypeScript, Rome was rewritten in Rust for performance and backed by Rome Tools Inc. When the company failed and employees were laid off, the core team found themselves locked out of the project’s infrastructure — they couldn’t access the npm registry, Discord server, or website hosting.

In August 2023, the team forked the project under a new name: Biome. The name combines “Bis” (meaning “encore” or “second”) with “Rome.” Since then, Biome has grown rapidly, surpassing 2.7 million monthly npm downloads and winning JSNation’s 2024 “Productivity Booster” award.

Why Replace ESLint and Prettier?

The ESLint + Prettier setup has served the community well, but it comes with friction:

  • Two tools, two configs: You maintain .eslintrc, .prettierrc, and often eslint-config-prettier to resolve conflicts between them.
  • Overlapping concerns: Both tools have opinions about code style, requiring careful configuration to avoid contradictions.
  • Performance: Both are JavaScript-based and process files relatively slowly, especially on large codebases. ESLint in particular can take minutes on monorepos.
  • Plugin sprawl: A typical setup requires eslint-plugin-react, eslint-plugin-import, @typescript-eslint/parser, @typescript-eslint/eslint-plugin, and more — each with its own versioning and compatibility matrix.

Biome consolidates all of this into a single binary with a single configuration file. There’s no plugin compatibility to manage, no conflicts between linter and formatter, and the Rust implementation makes everything dramatically faster.

Performance

The performance difference isn’t marginal — it’s transformative:

  • Formatting a large codebase completes in under 1 second
  • Linting 2,000 files with type-aware rules takes about 2 seconds
  • Without type-aware rules, linting drops to roughly 800 milliseconds

By comparison, ESLint + Prettier typically takes 10-30x longer on equivalent codebases. The speed difference is most noticeable in CI pipelines and pre-commit hooks, where developers are actively waiting for the tools to finish.

Biome achieves this through its Rust implementation and a daemon architecture. In editor environments, a long-running background process handles requests, eliminating the startup cost that slows down JavaScript-based tools on every invocation.

Getting Started

Setting up Biome is straightforward.

1. Install Biome

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

The --save-exact flag is recommended to ensure consistent behavior across your team, as formatting output can change between versions.

2. Initialize Configuration

npx @biomejs/biome init

This creates a biome.json file in your project root with sensible defaults:

{
  "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
  "formatter": {
    "enabled": true,
    "indentStyle": "tab",
    "indentWidth": 2,
    "lineWidth": 80
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  }
}

3. Run Biome

# Format files
npx biome format --write .

# Lint files
npx biome lint --write .

# Format, lint, AND organize imports in one pass
npx biome check --write .

The check command is the most useful — it runs everything at once and applies safe fixes automatically.

Configuration

Biome’s configuration lives in a single biome.json (or biome.jsonc for comments). Here’s a more complete example tailored for a typical React/TypeScript project:

{
  "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 100
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "complexity": {
        "noUselessFragments": "warn",
        "noForEach": "off"
      },
      "style": {
        "noNonNullAssertion": "warn"
      },
      "suspicious": {
        "noExplicitAny": "warn"
      }
    }
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single",
      "semicolons": "always",
      "trailingCommas": "all"
    }
  },
  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true,
    "defaultBranch": "main"
  },
  "files": {
    "includes": ["src/**", "tests/**"]
  }
}

Notice there’s no need to configure parsers for TypeScript or JSX — Biome handles all of that automatically. It also supports per-glob overrides, config inheritance via extends, and nested configs for monorepos.

The Formatter

Biome’s formatter covers JavaScript, TypeScript, JSX, TSX, JSON, JSONC, CSS, and GraphQL. It passed 97% of Prettier’s test suite, earning it the 2023 “Prettier Challenge” win.

There are a few intentional divergences from Prettier worth knowing about:

  • Default indent style is tabs (Prettier defaults to spaces). Configure with "indentStyle": "space" if you prefer spaces.
  • Object property unquoting is more aggressive — Biome uses ES2015+ identifier rules while Prettier sticks to ES5.
  • Invalid syntax handling — Biome’s stricter parser catches errors like duplicate modifiers that Prettier’s Babel parser silently accepts.

For most teams, the differences are negligible. If you’re migrating from Prettier, expect a one-time diff when reformatting, then consistent output going forward.

Suppressing formatting for specific code is done with comments:

// biome-ignore format: complex template literal alignment
const query = `
  SELECT * FROM users
  WHERE  id = ${userId}
  AND    active = true
`;

The Linter

Biome v2 ships with 455 rules organized into eight groups:

  • Accessibility — catches a11y issues (like missing alt text or invalid ARIA roles)
  • Complexity — suggests simplifications
  • Correctness — identifies definite bugs or dead code
  • Nursery — experimental rules, opt-in only
  • Performance — flags inefficient patterns
  • Security — detects potential vulnerabilities
  • Style — enforces consistent coding patterns
  • Suspicious — highlights likely incorrect code

Rules are sourced from ESLint core, typescript-eslint, eslint-plugin-react, eslint-plugin-jsx-a11y, eslint-plugin-unicorn, and others. Biome uses camelCase naming instead of ESLint’s kebab-case, so no-unused-vars becomes noUnusedVariables.

Type-Aware Linting

Biome v2 introduced type-aware linting that doesn’t require the TypeScript compiler. This is significant because typescript-eslint’s type-aware rules are notoriously slow — they need to spin up the TypeScript type checker. Biome achieves approximately 75% coverage of what typescript-eslint catches, with dramatically lower overhead.

Rules like noFloatingPromises — which catches unhandled promises — work without any tsconfig.json configuration:

// Biome catches this without TypeScript compiler
async function fetchData() {
  return await fetch('/api/data');
}

// Error: noFloatingPromises - This promise must be awaited or handled
fetchData();

Code Fixes

Biome distinguishes between safe and unsafe fixes:

# Apply safe fixes only (semantics-preserving)
npx biome lint --write .

# Apply safe AND unsafe fixes (may change behavior)
npx biome lint --write --unsafe .

Safe fixes are guaranteed to not change your code’s behavior — things like removing unused imports or simplifying expressions. Unsafe fixes might change semantics, like rewriting a for loop to Array.map. This distinction gives you confidence when running Biome in CI or pre-commit hooks.

Import Sorting

Biome v2 significantly improved import organizing. It merges duplicate imports from the same module, sorts import specifiers, handles import attributes, and even organizes exports:

// Before
import { useState } from 'react';
import { z } from 'zod';
import { useEffect } from 'react';
import type { FC } from 'react';
import { db } from '@/lib/db';

// After (biome check --write)
import type { FC } from 'react';
import { useEffect, useState } from 'react';

import { db } from '@/lib/db';

import { z } from 'zod';

Import organizing runs as part of biome check, so you don’t need a separate tool like eslint-plugin-import or @trivago/prettier-plugin-sort-imports.

Migrating from ESLint and Prettier

Biome provides automated migration commands that read your existing configuration and convert it:

# Migrate ESLint config
npx @biomejs/biome migrate eslint --write

# Migrate Prettier config
npx @biomejs/biome migrate prettier --write

The ESLint migration reads both legacy (.eslintrc) and flat config formats, resolves shared configurations and plugins, and maps ESLint rules to their Biome equivalents. Not every ESLint rule has a corresponding Biome rule, but the coverage is broad enough for most projects.

For a gradual migration, you can suppress all existing violations and fix them incrementally:

npx @biomejs/biome migrate eslint --write --suppress="suppressed due to migration"

This adds suppression comments to all current violations, letting you adopt Biome immediately while addressing issues over time.

After migration, you can safely remove your ESLint and Prettier dependencies:

npm uninstall eslint prettier eslint-config-prettier eslint-plugin-react \
  @typescript-eslint/parser @typescript-eslint/eslint-plugin \
  eslint-plugin-import eslint-plugin-jsx-a11y

That’s potentially 8+ fewer dependencies to maintain, audit, and keep compatible with each other.

IDE Integration

Biome has first-party extensions for VS Code, IntelliJ, and Zed, plus community extensions for Vim, Neovim, and Sublime Text.

The VS Code extension provides format-on-save, lint-on-save, code actions, and inline diagnostics. It communicates with the Biome daemon for fast responses — there’s no noticeable delay even on large files.

To set Biome as your default formatter in VS Code, add to your workspace settings:

{
  "editor.defaultFormatter": "biomejs.biome",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "quickfix.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  }
}

CI/CD Integration

Biome provides a dedicated ci command optimized for automated environments:

npx biome ci .

This command runs formatting checks, linting, and import organization, exiting with a non-zero code if any issues are found. It’s designed to be strict — no auto-fixing, just reporting.

For pull request workflows, Biome’s VCS-aware features are particularly useful:

# Only check files changed since the default branch
npx biome check --changed

# Only check staged files (great for pre-commit hooks)
npx biome check --staged

A simple GitHub Actions workflow looks like this:

name: Code Quality
on: [pull_request]

jobs:
  biome:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npx biome ci .

Language Support

As of Biome v2, here’s what’s supported:

Language Format Lint
JavaScript / TypeScript Yes Yes
JSX / TSX Yes Yes
JSON / JSONC Yes Yes
CSS Yes Yes
GraphQL Yes Nursery rules
HTML Experimental No

Notable absences: SCSS/Sass/Less are not supported (CSS only), and Vue/Svelte/Astro single-file components are on the roadmap but not yet available. If your project relies heavily on these, you may need to keep specific tools alongside Biome for now.

What’s New in Biome v2

The v2 release in June 2025 brought several major additions:

  • Plugin system: Write custom rules that match code patterns and report diagnostics. The distribution mechanism is still being developed, but it’s a significant step toward matching ESLint’s extensibility.
  • Assist actions: Code transformations like sorting object keys (useSortedKeys) and JSX attributes (useSortedAttributes) without diagnostic noise.
  • Range-based suppressions: Use // biome-ignore-start and // biome-ignore-end to suppress rules across a block of code.
  • Monorepo support: Nested biome.json files with per-package package.json awareness for automatic domain detection.
  • HTML formatter: Experimental, disabled by default — early stages but progressing.

Should You Switch?

If you’re starting a new project, Biome is an easy recommendation. One dependency, one config file, and formatting that completes before you can blink. The setup time savings alone make it worth adopting.

For existing projects, the decision depends on your ESLint plugin usage. If you rely on standard plugins like typescript-eslint, eslint-plugin-react, and eslint-plugin-jsx-a11y, Biome covers most of those rules and the automated migration makes switching straightforward. If you depend on niche ESLint plugins with no Biome equivalents, you might need to wait for the plugin ecosystem to mature.

Projects using SCSS or Vue/Svelte SFCs will need to keep their existing formatters for those file types, though Biome can still handle everything else.

Conclusion

Biome represents a new generation of frontend tooling — fast, unified, and opinionated in the right ways. By combining formatting, linting, and import sorting into a single Rust-powered binary, it eliminates the configuration complexity and performance overhead of the traditional ESLint + Prettier setup.

With v2’s type-aware linting, plugin system, and growing language support, Biome is well on its way to becoming the default choice for frontend projects. The migration path is smooth, the performance gains are immediate, and the reduced dependency footprint makes your project simpler to maintain.

‘Till next time!