Biome's Architecture and Tooling: biome.json, Editor Setup, CI and Monorepos
Key takeaways
Biome is a unified toolchain for JavaScript, TypeScript, JSON, CSS, and more—it lints, formats, and organizes imports from a single binary. This article covers core concepts, ESLint and Prettier migration, writing biome.json, and operating Biome in editors, CI, and monorepos from a practical perspective.
What This Article Covers
Biome aims to combine the roles of ESLint (linter) and Prettier (formatter)—common in frontend and full-stack codebases—into a single CLI and configuration file. It is implemented in Rust for speed and can run without a separate Node.js runtime, which makes a noticeable difference in large repositories.
This article walks through Biome’s core concepts and benefits, migration from ESLint and Prettier, biome.json configuration, VS Code and JetBrains integration, CI/CD pipelines, and monorepo setups in order. It also includes decision criteria and a practical checklist for teams that already run lint and format pipelines and are evaluating a new tool.
What Is Biome?
Core Concepts
Biome provides linter, formatter, and import organizer capabilities in one project. Users typically declare rules and style in a single biome.json (or biome.jsonc) and share the same settings via the CLI or editor extensions.
By design, it has the following characteristics:
- Single tool: One engine handles diagnostics and formatting to reduce conflicts between what the linter wants and what the formatter rewrites.
- Fast execution: Reducing wait time for repeated
lintandformatruns on large repositories is a major goal. - Consistent defaults: The workflow favors starting from a “recommended preset” and tuning only the rules you need.
Advantages Over ESLint and Prettier
Speed is the most common reason to adopt Biome. Previously, you might run ESLint and Prettier separately or go through two format/diagnostic cycles in the editor. Biome can combine multiple steps in one check or ci run, saving time locally and in CI.
Simpler configuration matters too. You can consolidate what was spread across .eslintrc, .prettierrc, plugin dependencies, and ignore rules into a biome.json-centric setup. Not every ESLint plugin rule has a direct Biome equivalent, so teams that need a fully identical rule set should plan a rule-gap analysis after migration.
For developer experience, official VS Code and JetBrains integrations provide LSP-based diagnostics, fixes, and formatting. Pinning Biome in package.json so the whole team uses the same version is typical.
Installation and Initialization
Install as a dev dependency from the project root. Pin the version with --save-exact:
npm install --save-dev --save-exact @biomejs/biome
npx @biomejs/biome init
init creates a default biome.json. Add scripts to package.json so the team uses the same commands:
{
"scripts": {
"check": "biome check .",
"check:write": "biome check --write .",
"format": "biome format --write .",
"lint": "biome lint .",
"ci": "biome ci ."
}
}
Think of biome check as the umbrella command that runs formatting, linting, import organization, and more. Locally, use --write to apply fixes; in CI, verify only without modifying files for safety.
Writing biome.json Configuration
Minimal Example
Below is a Biome 2.x baseline with recommended rules enabled. Extend files, vcs, and overrides for your project. Legacy Biome 1.x repos may still use older keys such as organizeImports.
{
"$schema": "https://biomejs.dev/schemas/2.4.12/schema.json",
"files": {
"ignoreUnknown": false,
"includes": ["**", "!!**/dist", "!!**/build", "!!**/.next", "!!**/coverage"]
},
"formatter": {
"enabled": true,
"indentStyle": "tab",
"indentWidth": 2,
"lineWidth": 100
},
"assist": {
"enabled": true,
"actions": {
"source": {
"recommended": true
}
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"semicolons": "always",
"trailingCommas": "all"
}
}
}
Point $schema at https://biomejs.dev/schemas/<version>/schema.json for your installed version, or at ./node_modules/@biomejs/biome/configuration_schema.json for local schema-based autocomplete.
Linter Rules and Severity
Under linter.rules, rules are grouped into categories such as style, suspicious, and correctness. Adjust specific rules to error, warn, or off by team agreement. If “Prettier-equivalent style” is the priority, align formatter options first and tighten lint rules gradually to reduce clashes.
VCS Integration (Respecting .gitignore)
Like ESLint and Prettier skipping Git-ignored files, Biome can integrate with your version control client via vcs settings. With biome.json at the repo root, enabling vcs.useIgnoreFile and similar options excludes unnecessary paths from analysis. CI must apply the same ignore rules so results match local runs.
{
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
}
}
Path-Specific Rules with overrides
To apply different rules to tests, scripts, or legacy folders, use include patterns in overrides. This mirrors ESLint’s overrides.
{
"overrides": [
{
"include": ["**/*.test.ts", "**/__tests__/**"],
"linter": {
"rules": {
"suspicious": {
"noExplicitAny": "off"
}
}
}
}
]
}
Migrating from ESLint and Prettier
Automated Migration Commands
Biome provides subcommands that read existing configs and write biome.json:
npx @biomejs/biome migrate eslint --write
npx @biomejs/biome migrate prettier --write
migrate eslint: Works with legacy.eslintrcand flat config within supported limits. Node.js may be required to resolve plugin dependencies. YAML ESLint configs may not be supported—convert to JSON/JS or map rules manually. ESLint rule names and behavior do not always match Biome 1:1.migrate prettier: Reads.prettierrcand similar. JSON5, TOML, or YAML Prettier configs may have migration limits—normalize to JSON-based config first for safer runs.
Biome may not migrate every “inspired” ESLint rule by default; consider --include-inspired if needed (it can add noise on large projects).
Post-Migration Checklist
- Run
biome checkon the whole repository and collect errors and warnings. - Replace reliance on ESLint comments (
eslint-disable) with Biome rule IDs. Comment systems differ, so hidden tech debt may surface. - Run
biome ciin CI for final verification. - Update team docs (contributing guides) with commands and editor settings for Biome.
Some teams run ESLint in parallel during transition, but two rule sets increase confusion. Prefer a timeline to converge on a single tool when possible.
VS Code Integration
Install the official Biome extension (biomejs.biome). Get it from the Visual Studio Code Marketplace or Open VSX (e.g. for VSCodium).
Format on Save, Safe Fixes, and Import Organization
Example workspace .vscode/settings.json:
{
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
}
}
source.fixAll.biome focuses on safe fixes. For unsafe fixes, adjust rule settings or apply them explicitly via the CLI.
Common Settings
biome.requireConfiguration: Whentrue, formatting and diagnostics register only whenbiome.jsonexists, so the extension does not interfere in unrelated folders.biome.lsp.bin: Useful in monorepos to point at a specific package’s Biome binary.
In multi-root workspaces, each folder can have its own Biome instance, which pairs well with monorepos that keep biome.json per package.
JetBrains Integration (WebStorm, etc.)
The Biome team ships an IntelliJ-family plugin (biomejs/biome-intellij). Search “Biome” in JetBrains Marketplace and install. Check the plugin page’s release notes for supported IDE versions (e.g. WebStorm).
How It Works and Key Settings
The plugin typically prefers the biome installed in the project’s node_modules. Pin @biomejs/biome in devDependencies so teammates do not rely on global installs.
Enable LSP-based formatting in settings so the formatter behaves consistently. To run fixes on save, configure IDE “actions on save” or plugin options alongside. As with VS Code, version skew between CLI and IDE is the most common source of mismatch—keep versions aligned via the repo.
CI/CD Pipeline Setup
Why biome ci
CI must not modify files, so biome ci is appropriate. It runs the formatter, linter, and import organization in read-only mode and exits non-zero when issues are found, failing the pipeline.
npx @biomejs/biome ci .
GitHub Actions Example
name: Biome
on:
pull_request:
push:
branches: [main]
jobs:
biome:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
- run: npm ci
- run: npx @biomejs/biome ci .
Checking Only Changed Files
On large repositories, you can scope checks to files changed in a PR to save time. Biome CLI --changed or --since-style options can limit the target to the diff against the default branch. Confirm exact flag names and behavior with biome ci --help for your Biome version.
For caching, use node_modules caches or Biome binary install caches. Teams should verify local npm run check and CI biome ci use the same Biome version first.
Monorepo Support
Since Biome v2, the configuration model officially supports monorepos. The idea is to use a root biome.json as the base and nest package-specific settings.
Root and Package-Level Configuration
Define shared rules and line width at the root:
{
"formatter": { "lineWidth": 100, "indentStyle": "space", "indentWidth": 2 },
"linter": { "enabled": true, "rules": { "recommended": true } }
}
For package-only rules (e.g. packages/logger), add biome.json in that folder and inherit from the root. The docs describe inheriting root config with the "extends": "//" microsyntax. To disable formatting for generated output, set formatter.enabled to false in that package.
Sharing Config with extends and npm Packages
You can place shared rules in common.json and reference them via extends from multiple apps. You can also expose Biome config from an npm package’s exports and reuse it as @org/shared-configs/biome. The package must resolve correctly under node_modules; resolution depends on the working directory where Biome runs.
Working Directory and Resolution
Biome walks from the file path toward the nearest biome.json. Choose whether to run everything from the root or per-package scripts to match your workflow. When running from the root, validate with sample PRs that nested package settings apply as expected.
Troubleshooting and Operations
- Version mismatch: Different Biome versions in local, CI, and IDE produce different diagnostics. Pin versions in
package.jsonand the lockfile; in the IDE, setbiome.lsp.binexplicitly. - Tabs vs spaces vs Prettier: Biome defaults may differ from Prettier. After
migrate prettier, recheckformatter.indentStyleandjavascript.formatterif team style diverges. - Too many rules: If warnings spike right after migration, start from
recommendedand enable categories gradually for easier maintenance. - Huge files: Generated files included in CI analysis slow runs down. Exclude them with
files.ignoreand VCS ignore integration.
Deciding whether Biome can replace ESLint in your repository
The deciding question is which of your current ESLint rules Biome cannot express. Run biome migrate eslint on a branch and read what it reports as unsupported or skipped. If the gaps are stylistic rules, accept Biome’s equivalents or drop them. If they include type-aware rules from typescript-eslint or framework plugins your team relies on to catch real bugs, you have two workable options: keep a trimmed ESLint config that runs only those rules alongside Biome, or postpone the switch. Running both with overlapping rules is the configuration that produces conflicting diagnostics.
Formatting is the other half. Biome’s formatter aims for Prettier compatibility but is not byte-identical, so land the reformat as a single commit, add that commit to .git-blame-ignore-revs, and switch CI to biome ci in the same change so nobody reintroduces the old formatting.
Frequently Asked Questions (FAQ)
Q. Why does Biome format some code differently from Prettier after migrating?
A. Biome’s formatter aims for Prettier compatibility, but a few edge cases still differ, and options you never ported keep their Biome defaults. Run biome migrate prettier --write to bring over options such as quote style and line width, reformat the whole repository in one dedicated commit, and add that commit to .git-blame-ignore-revs so blame history stays useful.
Related Articles
- Replacing ESLint and Prettier with Biome
- Husky v9 Git Hooks with lint-staged and commitlint
- Prettier in a Real Project
- ESLint 9 Flat Config