cheat sheet
@biomejs/biome
Package-level reference for @biomejs/biome on npm — install, single-binary toolchain replacing prettier+eslint, Node support, and alternatives.
@biomejs/biome
What it is
@biomejs/biome (scoped — note the @biomejs/ prefix) is a single Rust-compiled toolchain that combines a formatter (Prettier-compatible output) and a linter (a curated subset of ESLint + TypeScript-ESLint rules) into one binary, configured by a single biome.json. It started as a fork of Rome (which the original Rome team archived in 2023) and is now maintained by an independent community team.
It's roughly 25–35× faster than Prettier + ESLint on the same codebase because there's no Node startup, no plugin resolution, and parsing happens once per file across both passes. It's not yet feature-parity with Prettier + ESLint — coverage sits around the 95% mark — but it's the leading "single binary replaces both" option for new JS/TS projects.
Install
# Always a devDep — never ship Biome in your runtime bundle
npm install -D @biomejs/biome
pnpm add -D @biomejs/biome
yarn add -D @biomejs/biome
bun add -d @biomejs/biome
Output: biome binary on PATH under node_modules/.bin/biome.
# Initialise config
npx @biomejs/biome init
Output: writes a starter biome.json at the repo root.
# Day-to-day commands
npx biome format --write . # format in place
npx biome lint . # lint only
npx biome check --write . # lint + format + safe fixes in one pass
npx biome ci . # CI mode: error on any change, no writes
Output: Checked N files in <ms> summary; non-zero exit when issues remain.
Versioning & Node support
- Current major line is
2.x(released early 2025 — landed multi-file project analysis, plugin model, and the unifiedcheckcommand).1.xis in maintenance. - Requires Node 18+ to run the wrapper; the real binary is per-platform native code shipped as
@biomejs/cli-<os>-<arch>optional deps. - Single binary, no extras to install — coverage and language support land in the core release. Plugins (in
2.x) are GritQL queries, not npm packages. - Always a dev dependency.
- Strict semver — breaking rule defaults are gated behind major bumps.
Package metadata
- Maintainer: Biome team (community / independent core team, post-Rome fork)
- Project home: github.com/biomejs/biome
- Docs: biomejs.dev
- npm: npmjs.com/package/@biomejs/biome
- License: MIT
- First released: 2023 (as the Biome fork; predecessor Rome dates to 2020)
- Downloads: millions per week — fastest-growing lint/format tool on npm.
Peer dependencies & extras
@biomejs/biome has no formal peer deps and no companion packages to install:
| Surface | Notes |
|---|---|
| Native binary | Auto-installed per-platform via @biomejs/cli-darwin-arm64, @biomejs/cli-linux-x64, etc. as optionalDependencies. |
| Plugins | In 2.x, plugins are .grit files referenced from biome.json — no npm install step. |
| Editor integration | biomejs.biome for VS Code, biome.nvim for Neovim, IntelliJ plugin — install through the editor, not npm. |
| CI action | biomejs/setup-biome GitHub Action — pins Biome version without touching package.json. |
pre-commit | Direct integration via biomejs/pre-commit hook (no Node wrapper required). |
The --apply mode (legacy) was renamed --write in 2.x; --apply-unsafe became --write --unsafe. The unified check --write pass replaces the prettier+eslint+lint-staged chain.
Alternatives
| Tool | Trade-off |
|---|---|
| prettier + eslint | The incumbent stack — wider rule coverage, larger plugin ecosystem (Tailwind plugin, MDX plugin, every framework). Slower; two configs; coordinating versions is annoying. |
| dprint | Rust formatter (no linter), plugin-based, very fast. Pick if you only want formatting and want explicit plugin control. |
| oxc + oxlint | Rust-based JS toolchain by Boshen / VoidZero. oxlint is a much faster ESLint-compatible linter (uses .eslintrc / flat config). Pair with Prettier for formatting. Newer, narrower coverage than Biome. |
| rome | Archived in 2023 — Biome is the successor. Don't start new projects with it. |
| deno fmt + deno lint | Bundled with Deno. Comparable to Biome in feel; tied to Deno's ecosystem and rule set. |
Common gotchas
- Not yet at full Prettier/ESLint parity. Coverage is roughly 95%. The known gaps: some Prettier plugins (Tailwind class sorting, MDX) have no Biome equivalent (the GritQL plugin model in
2.xis closing this), and ESLint plugins for niche frameworks aren't ported. Check the rules-coverage page before migrating from a complex ESLint config. - Single config format only.
biome.json(orbiome.jsonc) is the only config — nobiome.config.js, nopackage.jsonfield. Cannot share programmatic config across monorepos the way aneslint.config.jsextendsarray can; use theextendsfield with a path to another JSON file instead. - Editor on-save needs the right command. VS Code's "Format on Save" runs the formatter only. To get lint auto-fixes too, wire the
quickfix.biomecode action — otherwise you'll see lint warnings even after Save and wonder why nothing's fixing them. organizeImportsis on by default. Biome reorders imports on everycheck --write, which churns git history on the first run. Land that change in a single dedicated commit before adopting Biome on a busy repo.- Rule customisation is narrower than ESLint. Biome rules typically take only
off/warn/errorand a smalloptionsobject — far less configurable than ESLint's per-rule schema. Some teams' ESLint configs aren't expressible in Biome. --unsafedoes what it says. Unsafe fixes change runtime behaviour (e.g. removing dead code that may have side effects). Never run--write --unsafein CI; reserve it for local one-shot sweeps and review the diff.- Old
--applyflag is deprecated.1.xused--apply/--apply-unsafe.2.xswitched to--write/--write --unsafe. CI scripts copied from older blog posts silently no-op (Biome ignores unknown flags) until you upgrade.
Real-world recipes
The realistic migration paths off prettier+eslint, and the patterns Biome makes possible.
Drop-in migration from prettier+eslint
Biome ships migrate subcommands that convert existing config:
# Translate .prettierrc → biome.json formatter options
npx @biomejs/biome migrate prettier
# Translate .eslintrc.* → biome.json linter rules
npx @biomejs/biome migrate eslint
# After both:
npx biome check --write . # land the first big diff in one commit
Output:
✔ Wrote biome.json from .prettierrc
✔ Wrote biome.json from .eslintrc.json (47 rules mapped, 3 unsupported)
Checked 312 files in 184ms. Fixed 287 files.
The migrator handles ~80% of common configs. Rules without a Biome equivalent are flagged in the output — keep ESLint running on those files only, or accept the gap.
Land the "first big diff" cleanly
Biome's organizeImports and formatter changes can produce a 10,000-line diff on the first run. To keep history reviewable:
git checkout -b chore/adopt-biome
npx biome check --write .
git add -A
git commit -m "chore: format codebase with Biome (mechanical)"
# Configure CI to fail on drift
echo '"scripts": { "format:check": "biome ci ." }' >> package.json
Output:
Switched to a new branch 'chore/adopt-biome'
Checked 312 files in 184ms. Fixed 287 files.
[chore/adopt-biome 7a3f1c8] chore: format codebase with Biome (mechanical)
287 files changed, 4821 insertions(+), 4502 deletions(-)
Reviewers can skip the mechanical commit; subsequent PRs only carry the human changes.
Monorepo with shared config
Biome supports extends from a path:
{
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"extends": ["../../biome.base.json"],
"files": { "include": ["src/**"] },
"linter": { "rules": { "style": { "useNamingConvention": "error" } } }
}
Each workspace overrides what it needs without duplicating the base.
Per-language overrides
{
"javascript": { "formatter": { "quoteStyle": "double", "semicolons": "always" } },
"json": { "formatter": { "indentWidth": 2 } },
"css": { "formatter": { "quoteStyle": "single" } }
}
Custom GritQL plugins (v2)
Biome v2's plugin model accepts .grit query files — declarative AST patterns that match and rewrite code. No npm install step; reference the file from biome.json:
{ "plugins": ["./tools/lint/no-console-log.grit"] }
// tools/lint/no-console-log.grit
language js
`console.log($args)` as $log => `// removed: console.log($args)`
Limited compared to ESLint custom rules, but covers the common "ban this AST shape" use cases.
Lint only changed files in pre-commit
For large repos where running biome check . on every commit is too slow:
# .lintstagedrc.yaml
"*.{js,jsx,ts,tsx,json,css}":
- "biome check --write --no-errors-on-unmatched"
lint-staged passes only the staged files. Biome processes them in-place, and the formatter changes are re-staged.
Wire Biome alongside ESLint during migration
{
"scripts": {
"lint": "biome check . && eslint .",
"lint:fix": "biome check --write . && eslint . --fix"
}
}
Biome handles 95% of rules fast; ESLint covers the long tail. The two-tool overlap costs ~2× the runtime but lets you migrate file-by-file rather than big-bang.
Production deployment
Biome is a dev-time tool — there's no production runtime. But CI integration matters.
CI as a gate
# .github/workflows/lint.yml
- uses: biomejs/setup-biome@v2
with: { version: latest }
- run: biome ci .
biome ci is a strict subcommand: no writes, exits non-zero on any issue, machine-readable output. Faster than biome check because it pre-allocates parallel workers based on --max-diagnostics.
Pin the version explicitly
Biome's defaults occasionally shift between minors. Pin exactly:
npm install -D --save-exact @biomejs/biome
Output:
added 1 package, and audited 142 packages in 3s
A renovate/dependabot config can bump it on a cadence — but the pinned version means CI never breaks because someone's lockfile drifted.
Pre-commit integration
# .pre-commit-config.yaml
repos:
- repo: https://github.com/biomejs/pre-commit
rev: v2.0.0
hooks:
- id: biome-check
args: [--write, --no-errors-on-unmatched]
No Node wrapper required — the pre-commit hook downloads the native binary directly.
Performance tuning
Biome is fast — but a few knobs matter on huge repos.
File discovery
biome check . walks the file tree once. On monorepos with many node_modules/, restrict the search:
npx biome check src/ # explicit path beats globs
Output:
Checked 187 files in 92ms. No fixes needed.
biome.json files.ignore accepts gitignore-style patterns:
{ "files": { "ignore": ["**/node_modules/**", "**/dist/**", "**/.next/**"] } }
--max-diagnostics
Default is 20 — Biome stops collecting after 20 issues to keep output readable. Bump to see all:
npx biome check --max-diagnostics=1000 .
Output:
Checked 312 files in 184ms.
Found 47 errors, 12 warnings across the project.
Worker threads
Biome auto-fans out across CPU cores. Override on memory-constrained CI:
npx biome check --threads=2 .
Output:
Checked 312 files in 421ms. No fixes needed.
vs. node-tool chain
A single Rust binary doing parse-once-lint-and-format-twice beats two Node processes that parse the same files independently. Typical wins:
- Cold cache: 25-35× faster than
eslint . && prettier --check . - Warm: 10-15× (ESLint's cache narrows the gap)
- CI: 2-5× (download cost amortised)
ESM/CJS interop & bundling
Biome is not a bundler — it doesn't ship runtime code, it just processes source. The npm package is a thin Node wrapper that locates the native binary; the binary itself is per-platform via optionalDependencies.
You install one logical package (@biomejs/biome); npm picks @biomejs/cli-darwin-arm64, @biomejs/cli-linux-x64, or similar. Air-gapped CI: pre-download all per-platform binaries or use the standalone curl install.
# Standalone install — no Node needed
curl -fsSL https://github.com/biomejs/biome/releases/latest/download/biome-linux-x64 -o /usr/local/bin/biome
chmod +x /usr/local/bin/biome
Output: (none — exits 0 on success)
Version migration guide
| From → To | Highlights |
|---|---|
| Rome → Biome 1.0 | Project forked from archived Rome in 2023. Existing rome.json configs can be renamed to biome.json. |
| 1.x → 1.5 | First-class CSS support, useImportType rule. |
| 1.5 → 1.9 | assist namespace introduced (replacing organizeImports). New JSX rules. |
| 1 → 2 | The big one. Multi-file project analysis. GritQL plugin system. --apply renamed to --write; --apply-unsafe → --write --unsafe. Some rule defaults tightened. assist.actions.source.organizeImports replaces the v1 top-level flag. |
Common 1→2 migration friction
- CI scripts copied from blog posts still pass
--apply— Biome silently ignores unknown flags. Symptom: CI passes, files weren't actually formatted. Audit every script after upgrading. organizeImportsis now underassist.actions.source.organizeImports: "on". The old top-level key is removed.- Rule renames — a handful of rule IDs changed namespace.
biome migratefrom a v1 config to v2 handles most renames automatically.
Security considerations
- Pin the binary version. Auto-update without review means a new rule could silently downgrade severity.
--save-exact+ Renovate auto-PRs is the safe pattern. - GritQL plugins execute against source. A malicious
.gritfile in a dep could rewrite code onbiome check --write. Audit plugins like you'd audit ESLint plugins. --unsafefixes change behaviour. Never wire--write --unsafeinto pre-commit or CI. Reserve for one-shot sweeps with manual review.biome ciis reproducible. Same binary version + same config + same source = same output. CI failures point to either a config drift or a rule change in a new Biome version, not non-determinism.- Editor integration runs the same binary. VS Code's Biome extension shells out to
node_modules/.bin/biome(the wrapper picks the right per-platform binary). No separate "language server" with elevated privileges.
Configuration patterns
Minimal biome.json
{
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"formatter": { "enabled": true, "indentStyle": "space", "indentWidth": 2 },
"linter": { "enabled": true, "rules": { "recommended": true } },
"assist": { "actions": { "source": { "organizeImports": "on" } } }
}
Per-glob override
{
"overrides": [
{ "include": ["**/*.test.ts"], "linter": { "rules": { "style": { "useNamingConvention": "off" } } } },
{ "include": ["**/*.config.ts"], "linter": { "rules": { "suspicious": { "noExplicitAny": "off" } } } }
]
}
VCS integration
vcs.useIgnoreFile: true tells Biome to honour .gitignore — no need to duplicate exclude patterns:
{
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }
}
biome.jsonc for comments
Biome accepts JSON-with-comments (biome.jsonc) when you want to annotate decisions. Pick one form per repo — running both produces an "ambiguous config" warning.
Troubleshooting common errors
- Rule name not recognised after migration — Biome uses a different rule taxonomy than ESLint. Run
biome explain <rule>to confirm the name. The migration tool maps most but not all. Format on Savenot formatting — VS Code's "Default Formatter" is set to Prettier. Set it to "Biome" globally or per-language.biome checksucceeds, but files aren't fixed — missing--write. Default is read-only.- CI fails with "unknown option
--apply" — that flag was removed in v2. Use--write. - Editor lints don't match CLI — editor reads
biome.jsonfrom the workspace root; CLI may pick a different config if run from a subdir. Verify both find the same file.
Ecosystem integrations
Biome ships everything in one binary, so the "ecosystem" is editors, CI actions, and pre-commit hooks rather than npm plugins:
| Integration | What it covers |
|---|---|
biomejs.biome (VS Code) | Format-on-save, code-action quickfix, status bar |
biome.nvim (Neovim) | LSP wrapper |
| JetBrains plugin | First-party — WebStorm / IntelliJ |
biomejs/setup-biome (GitHub Action) | Pin version, install binary |
biomejs/pre-commit | Git pre-commit hook |
dprint-plugin-biome | Run Biome inside dprint for mixed-format repos |
GritQL plugins are the only "extensibility" — JS-based plugins are not on the roadmap.
When NOT to use this
Biome is excellent for new projects and most app codebases. The cases that still need ESLint:
- Niche ESLint plugins.
eslint-plugin-jest,eslint-plugin-storybook,eslint-plugin-svelte(Svelte-specific rules) have no Biome equivalent yet. Coverage is closing every release; check the rule-sources matrix before migrating. - Custom JS rules. GritQL is declarative — patterns only, no procedural logic. Custom ESLint rules that walk the AST programmatically don't port.
- Tailwind class sorting.
prettier-plugin-tailwindcssis still the only solid option for sortingclass="..."strings. Run Biome for everything else, Prettier just for*.tsxTailwind sorting. Or accept unsorted classes. - Long-tail language support. Biome covers JS/TS/JSX/JSON/CSS/GraphQL/Markdown (partial). For Vue SFC, Astro, Svelte, MDX you still need Prettier plugins.
- Existing huge ESLint configs. If migrating produces a 30% rule-coverage gap, the maintenance cost of two tools may exceed the speed win. Stay on ESLint until coverage closes.
See also
- JavaScript: biome — config, rules, migration recipes
- JavaScript: prettier — the formatter Biome replaces
- JavaScript: eslint — the linter Biome replaces
- Packages: npm-prettier
- Packages: npm-eslint