Five config files down to one biome.json — what you gain and what you keep when replacing ESLint and Prettier with Biome 2.x
Every time I set up a new TypeScript project, I've repeated the same routine. Create .eslintrc.js, wire it with eslint-config-prettier so the two tools don't conflict, add @typescript-eslint/* dependencies, sort imports with eslint-plugin-import, and write a separate .prettierrc. By the time you're done, you've got five config files and nearly ten related packages. I used to think this was just "the normal dev environment."
Biome 2.x compresses all that complexity into a single biome.json file. A single binary written in Rust handles linting, formatting, and import sorting in one pass, and through its own type inference engine it even supports some type-aware lint rules (though not at the same level as the full TypeScript compiler — more on that later). The Biome v2 official release introduced type-aware linting, GritQL-based plugins, and a domain system, and as of 2026 the v2.5 release supports over 500 cumulative rules and cross-file linting.
This article walks through the concrete migration process alongside what you gain and where you should stop, illustrated with real config examples.
The Complexity That ESLint + Prettier Built Up
The Combination Was the Problem, Not the Tools
Here's the list of relevant files in a typical project that uses ESLint and Prettier together.
.eslintrc.js(oreslint.config.js).eslintignore.prettierrc(orprettier.config.js).prettierignoretsconfig.json—parserOptions.projectsetting for type-aware rules
On top of that, packages like @typescript-eslint/parser, @typescript-eslint/eslint-plugin, eslint-config-prettier, and eslint-plugin-import pile up. Managing version compatibility between each package is surprisingly tricky — I've personally experienced other plugins breaking after upgrading @typescript-eslint more than once.
Three Things That Changed in Biome v2
Back in v1, Biome didn't have enough rules to be a viable ESLint replacement. The v2 release added three decisive features.
Type-aware linting (partial support): Previously, using type-based rules like no-floating-promises or await-thenable with @typescript-eslint required feeding tsconfig.json to the parser so tsc could read type information. Biome v2 introduces its own type inference engine to apply some type-aware rules without tsc. However, this inference engine is not equivalent to the TypeScript compiler — it's limited to what Biome can process — so tsc is still required for full type checking.
GritQL plugins (experimental as of 2026): Starting with v2, you can write custom lint rules using GritQL. As stated in the Biome Linter Plugins official documentation, this is still an experimental feature, so you should account for possible spec changes before adopting it in production.
Domain system: It analyzes package.json dependencies and automatically activates React-related rules for projects using React, and Next.js-specific rules for Next.js projects. You don't need to register plugins individually in your config.
The Biome project receives sponsorship from various companies, though the tier and form of sponsorship (monetary sponsors, infrastructure support, public case studies, etc.) varies by company. For accurate sponsorship details, I recommend checking the Biome official site's sponsors page directly.
Migration — How It Actually Goes
Biome provides a migrate subcommand that automatically converts your existing config. Handling Prettier first, then ESLint, reduces conflicts.
# 1. Install with exact version (--save-exact recommended)
npm install --save-dev --save-exact @biomejs/biome
# 2. Initialize biome.json
npx @biomejs/biome init
# 3. Convert Prettier config → biome.json
npx @biomejs/biome migrate prettier --write
# 4. Convert ESLint config → biome.json
npx @biomejs/biome migrate eslint --write
# 5. Validate the conversion result
npx @biomejs/biome check .Three Easy-to-Miss Points
First, Prettier default differences. Biome uses tab indentation by default. If your existing project was using spaces, migrate prettier will convert it, but when setting up fresh you need to specify it explicitly.
Second, differences in recommended rule sets. migrate eslint maps rules explicitly defined in your existing ESLint config to their Biome equivalents. However, ESLint's eslint:recommended set and Biome's recommended set are entirely separate sets with different rule compositions from the start. Therefore, if you want to use Biome's recommended set after migration, you need to explicitly enable linter.rules.recommended in biome.json. I remember not knowing this at first and wondering "why are there so few rules?"
Third, migrate prettier cannot read Prettier config files in JSON5, TOML, or YAML format. If your .prettierrc is in a non-JSON format, you'll need to migrate it manually.
Packages You Can Remove After Migration
npm uninstall eslint prettier \
eslint-config-prettier \
eslint-config-next \
eslint-plugin-react \
eslint-plugin-import \
@typescript-eslint/parser \
@typescript-eslint/eslint-pluginYou can also remove .eslintrc.js, .eslintignore, .prettierrc, and .prettierignore files.
Deep Dive into biome.json Configuration
Configuring Lint + Format + Import Sorting All at Once
Below is a conceptual example for the v2.5 series as of 2026. Adjust the $schema URL to match the version you actually installed, and check valid values for each option in the Biome configuration reference.
{
"$schema": "https://biomejs.dev/schemas/2.5.0/schema.json",
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"assist": {
"actions": {
"source": {
"organizeImports": "on"
}
}
}
}As stated in the organizeImports official documentation, the import sorting path moved to assist.actions.source.organizeImports starting in v2 (in v1 it was a top-level organizeImports section). This handles the import order sorting and import merge/split comment processing that was previously handled by eslint-plugin-import.
If you want to use the domain system (react, next, etc.) alongside this, I recommend verifying the exact key-value format in the schema before enabling it. Since supported domains and value formats may change across minor versions, the example above deliberately shows the minimal safe configuration with only recommended enabled.
Monorepo Config Inheritance
Biome supports placing multiple biome.json files within a workspace and having them inherit and extend each other. The exact field names (extends, root) and the format of values for each field are documented in the Biome guide for large projects, so when adopting it in a monorepo, it's safest to reference the examples in that document directly. Depending on the documentation, you'll use relative paths or workspace directives, and since the exact syntax may change across minor versions, verify the installed version's documentation before copy-pasting.
CI Integration — Handled in a Single Pass
You can replace the CI steps that previously ran ESLint and Prettier separately with the following.
- name: Biome check
run: npx @biomejs/biome ci .Unlike biome check, biome ci only inspects files without modifying them. Since it handles formatting, linting, and import sorting in a single pass, it reduces CI execution time.
The actual speed difference varies significantly depending on project size, runner specs, file count, and the rule sets in use. Benchmark figures are published in various places, but measurement conditions differ, so when deciding whether to adopt it, the most reliable approach is to run a direct comparison in your own project using the time command.
What Can Be Replaced, and Where Should You Stop
Advantages
| Item | Details |
|---|---|
| Speed | Rust-based multi-core parallel processing, no Node.js startup cost (actual figures vary by project) |
| Config simplification | 5+ config files → 1 biome.json |
| Type-aware linting (partial) | Some type-based rules applied without tsc |
| Single toolchain | Lint, format, and import sorting handled in one pass |
Caveats
| Item | Details |
|---|---|
| Type inference scope | Biome's own inference engine is not equivalent to the TypeScript compiler. Full type checking still requires tsc |
| Prettier compatibility | Output is mostly compatible but not 100%. Diffs possible due to default differences (tabs vs spaces) and a small number of edge cases |
| Plugin ecosystem | More limited than ESLint. import/no-cycle, eslint-plugin-security, eslint-plugin-jest, etc. are unsupported or experimental |
| Vue / Svelte / Astro | Support level as of 2026 must be verified at the official documentation |
| JSON5 / YAML Prettier config | Cannot be auto-converted by migrate prettier, requires manual work |
| GritQL plugins | Experimental feature. Spec stability must be reviewed before production adoption |
When You Can't Fully Drop ESLint
If you can't immediately drop all ESLint plugins, you can run both tools side by side. What you need in that case is a configuration that disables ESLint rules that overlap with Biome (such as formatting-related rules). The following is a conceptual example — when using it in practice, verify the export format and name in the latest documentation of the config package you'll use for the parallel setup.
// eslint.config.js — conceptual example
export default [
// Add config here that disables rules conflicting with Biome
// Keep only specialized plugins not yet supported by Biome
];This allows a gradual migration where formatting and basic linting are delegated to Biome, while only the specialized rules Biome doesn't yet support are kept in ESLint.
Wrapping Up with Decision Criteria
I've fully migrated to it on a React + TypeScript project and haven't encountered any major pain so far. Just having fewer config files has noticeably reduced the maintenance burden, and having the lint and format steps finish quickly in CI is a nice bonus too.
Ultimately, the decision between a full migration and running both tools comes down to two criteria.
- Do the ESLint plugins you depend on have equivalent rules in Biome? If you rely on rules that can't be replaced — such as
import/no-cycle,eslint-plugin-security, or project-specific custom rules — running both tools in parallel is the realistic option. - Is your framework within Biome's parser support? If you're primarily working with JavaScript, TypeScript, JSX, and TSX, migration is straightforward. But if Vue, Svelte, or Astro makes up a significant portion of your codebase, check the support level in the official documentation at the time of adoption before deciding.
If both criteria are green, go for a full migration. If even one is a concern, it's safer to start with parallel operation using eslint-config-biome and migrate incrementally.