Biome: one tool for linting and formatting
By Flavio Copes
Biome replaces ESLint and Prettier with one fast Rust tool. Install with biome init, run biome check, and migrate your existing config.
Biome is a formatter, a linter, and an import organizer in one native binary, with one configuration file.
For many JavaScript and TypeScript projects it can replace ESLint plus Prettier. It is written in Rust and it is fast, but speed is rarely the deciding factor. The question to answer before switching is whether Biome covers the rules and the file types your project depends on.
What each part does
The formatter rewrites code into a consistent style. The linter reports suspicious or incorrect patterns. Assist actions apply source transformations, and the one you will use most is organizing imports.
biome check runs all three. biome format and biome lint run one at a time.
Biome parses each file once and shares the result between the three. With ESLint and Prettier you have two parsers and two configurations. You usually add eslint-config-prettier to stop lint rules from conflicting with the formatter. Biome coordinates both jobs inside the same tool.
Install Biome in the project
Add Biome as an exact development dependency:
npm install --save-dev --save-exact @biomejs/biome
--save-exact pins the version, so every developer and CI run the same binary. When you want a newer one, bump it on purpose.
Check the installed version:
npx biome version
Create the configuration:
npx biome init
This writes biome.json in the project root.
Use biome.jsonc when you want comments:
npx biome init --jsonc
Commit the configuration and lockfile.
Start with a small configuration
Here is a practical biome.json for the style used in these examples:
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"includes": [
"src/**",
"tests/**",
"scripts/**",
"*.json"
]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"semicolons": "asNeeded"
}
}
}
The schema path points at the installed package. It stays aligned with the version in node_modules without putting an exact release number in the URL.
The VCS section tells Biome to respect Git ignore files. The files.includes list defines which files Biome should inspect.
Start with the recommended rules, and add one only when it would have caught a real bug in your project.
Check the project
Run all configured checks:
npx biome check .
Without --write, Biome reports formatting differences, lint diagnostics, and import organization changes. It does not rewrite files.
On an existing project, run this first. It shows how big the diff will be before you change anything.
Apply formatting, import sorting, and safe fixes:
npx biome check --write .
Review the diff after any bulk write:
git diff
Biome separates safe and unsafe fixes. --write applies safe fixes. Add --unsafe only when you have reviewed what those rules may change:
npx biome check --write --unsafe .
I would never make --unsafe the automatic default in a pre-commit hook.
Run formatting alone
Check formatting without writing:
npx biome format src/
Format files:
npx biome format --write src/
The formatter is opinionated, like Prettier. You get a handful of options for quotes, semicolons and line width, and that’s it. The point is to stop arguing about line breaks.
Biome tries to stay close to Prettier, but the output is not byte-for-byte identical in every case. Expect a formatting diff during migration.
Run linting alone
Run the linter:
npx biome lint src/
Apply safe lint fixes:
npx biome lint --write src/
Show the documentation for a rule:
npx biome explain noUnusedVariables
Diagnostics include a category such as:
lint/correctness/noUnusedVariables
Use that category when configuring or suppressing the rule.
Configure one rule
Rules are grouped by purpose. This configuration rejects console.log calls:
{
"linter": {
"rules": {
"recommended": true,
"suspicious": {
"noConsole": "error"
}
}
}
}
Rule names and groups are part of Biome’s own configuration model. An ESLint rule with a similar purpose may have another name.
Before copying a rule from a snippet, check the current Biome rule page. It shows stability, default severity, supported languages, and whether a fix is safe.
Suppress a diagnostic narrowly
Sometimes a rule is correct for the project but wrong for one line.
Use a targeted suppression with a reason:
// biome-ignore lint/suspicious/noConsole: this CLI writes its result to stdout
console.log(report)
The comment names one rule and explains the exception.
Avoid a file-wide suppression when one line is enough. Avoid disabling a recommended group because of one false positive.
Configure different folders
Tests may need rules that production code does not. Use an override:
{
"overrides": [
{
"includes": ["tests/**"],
"linter": {
"rules": {
"suspicious": {
"noConsole": "off"
}
}
}
}
]
}
Keep overrides few. Once several patterns overlap, it gets hard to know which rule applies to a given file.
React rules
Biome includes rules for React Hooks, including hook placement and effect dependencies.
Current Biome versions organize framework-specific rules into domains. A React project can enable the recommended React domain:
{
"linter": {
"domains": {
"react": "recommended"
}
}
}
That covers the common rules. It does not mean every rule from every React ESLint plugin has a Biome equivalent, so compare the exact list your project uses before you remove ESLint.
Migrate from Prettier
Commit or stash your work before a migration. Then start from a clean tree so the generated diff is easy to review.
Ask Biome to read the Prettier configuration:
npx biome migrate prettier --write
Biome maps supported settings into biome.json. Its defaults differ from Prettier in areas such as tabs and semicolons, so migration is better than assuming the defaults match.
Run a formatting check next:
npx biome format .
Then format on a branch and review the complete diff:
npx biome format --write .
Biome does not format every language or framework Prettier supports. Keep Prettier for the files Biome doesn’t cover, and migrate gradually if you need to.
Migrate from ESLint
Biome can read legacy and flat ESLint configuration:
npx biome migrate eslint --write
The migration understands several popular plugin rule sets and maps supported rules into Biome names.
It cannot reproduce arbitrary JavaScript inside an ESLint config, custom rules, processors, or every plugin behavior. Current Biome documentation also notes configuration-format limits, so check the migration guide for the config used by your project.
Compare the actual enabled rules before uninstalling anything:
- Run the current ESLint command and save its result
- Run the Biome migration
- Run
biome checkwithout writing - Review unsupported and changed rules
- Keep ESLint for gaps that matter
- Remove old dependencies only after CI passes
Using both tools temporarily is better than silently losing a security or framework rule.
Add package scripts
Give local development and CI stable commands:
{
"scripts": {
"check": "biome check .",
"check:write": "biome check --write .",
"ci": "biome ci ."
}
}
Run the local check:
npm run check
Fix safe issues:
npm run check:write
The scripts always run the Biome version installed in the repository.
Use biome ci in automation
Biome provides a separate read-only command for CI:
npx biome ci .
It runs formatting, linting, and import organization checks without offering a write mode.
A GitHub Actions job can be small:
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: npm
- run: npm ci
- run: npm run ci
Don’t let CI rewrite and commit code. Its job is to fail when the committed source does not follow the rules, so you fix it locally.
For a large repository, Biome can check only changed files through its VCS integration. Check the complete project until you trust the rules and ignore patterns.
Check staged files
Biome has a staged mode:
npx biome check --staged
This is useful in a Git hook because it focuses on the next commit.
Use the read-only command in the hook first. Automatic writes and partially staged files need careful testing, because the working-tree file may contain changes that are not part of the commit.
The Git hooks tutorial explains how the staged snapshot differs from the working tree.
Editor integration
Install the official Biome extension in VS Code or Cursor. Set Biome as the default formatter for the languages the project assigns to it.
The editor and CLI must use compatible Biome versions and the same biome.json. Prefer the project’s installed binary when the extension supports that choice.
Enable format on save only after the first repository-wide formatting decision. Otherwise, one file may receive a large unrelated diff during normal work.
Run the CLI before committing even with editor integration. The CLI is the shared source of truth and covers files nobody opened.
Generated files and ignores
Do not lint build output, vendored code, or generated clients unless the project owns their source.
Use files.includes with ordered exceptions:
{
"files": {
"includes": [
"**",
"!dist/**",
"!coverage/**",
"!src/generated/**"
]
}
}
Also enable vcs.useIgnoreFile so Biome respects your Git ignore files too.
Be explicit in monorepos. A root configuration should not accidentally scan caches or generated files inside every package.
Common Biome mistakes
Most problems come from treating the switch as a rename. Someone deletes ESLint before comparing rule coverage, or expects the formatter output to match Prettier byte for byte, then discovers a framework plugin rule that Biome does not have.
The other group is about configuration. You run --write --unsafe without reading the diff, or files.includes is too broad and Biome scans dist/ and node_modules/. A globally installed Biome doesn’t match the version in the lockfile, or the editor extension and CI load different configurations. Or you add many overrides before anyone understands the defaults.
None of these are hard to fix, but they are easy to miss on the first day.
When to keep ESLint or Prettier
Keep ESLint when the project relies on a custom rule, a processor, or a framework plugin Biome does not cover.
Keep Prettier for file types Biome cannot format. Different tools on non-overlapping file sets is fine. Two formatters on the same JavaScript files is not, because even small output differences mean every save produces churn.
A new project is the easiest place to adopt Biome, because there is nothing to migrate. An older repository with years of ESLint config needs the rule-by-rule comparison above.
How I would use Biome
On a new project I would run biome init, set single quotes and asNeeded semicolons, keep the recommended rules, and stop there until something real comes up.
On an existing project I would do the migration on its own branch, and in two commits: first the formatting-only change, which is large and boring, then any lint fixes that change behavior. Reviewing them together is painful.
If a rule I depend on is missing, I would keep ESLint next to Biome for that rule rather than drop it. One tool is nicer than two, but not if it stops checking something the project needs.
Want me to talk about your product? You can sponsor this site.