Biome – Unified Linting & Formatting for JavaScript/TypeScript in Rust
Biome merges linting and formatting into a single Rust engine, cutting CI time and simplifying configs. Learn how to migrate, benchmark, and what to watch for.
11 Jan 2026, 13:28 UTC

Concrete Problem: Too Many Tools, Too Much Overhead
Modern JS/TS projects normally run eslint for linting and prettier for formatting. Each tool reads the source twice, parses the file, and writes back the result. In a CI pipeline that touches hundreds of files, that double parsing adds measurable latency and creates two separate configuration files (.eslintrc and .prettierrc). The result is a fragile build that can fail for subtle formatting differences or rule mismatches.
Thesis: Biome Combines Linting and Formatting Into One Fast Pass
Biome is a Rust‑based toolchain that parses the Abstract Syntax Tree (AST) once and runs both linting and formatting rules in that single pass. It eliminates the need for separate config files, reduces CI runtime, and keeps a single source of truth for code quality.
1. How Biome Works
Biome’s architecture is intentionally minimal:
- Single AST Parse – The parser is shared between linting and formatting engines.
- Rule Engine – Lint rules are expressed as simple AST visitors; formatting rules are pattern‑based rewrites.
- Parallel Execution – Workers process files concurrently, bounded by the number of CPU cores.
- Zero‑Configuration Default – A sensible
biome.jsonis generated automatically; you only override what you need.
Because the engine is written in Rust, it is memory‑safe and avoids the overhead of a JavaScript runtime. The result is a tool that is often an order of magnitude faster than running eslint and prettier sequentially.
2. Migration Path – From Prettier + ESLint to Biome
- Install Biome
curl -fsSL https://biomejs.dev/install.sh | sh # or via npm npm i -g biomeBiome installs a binary that you run from the command line. It requires
sudoonly if you install globally; otherwisenpx biomeworks. - Generate Default Config
biome initThis creates
biome.jsonwith default lint and format rules. Review the file – you can comment out rules you don’t need. - Run a Fix‑All Pass
biome check --applyRun this in a local clone to see which files are touched. The
--applyflag tells Biome to write changes back to disk. After the run, commit the modified files. - Benchmark the Speed
Use a simple benchmark script to compare runtimes:
# biome vs eslint+prettier TIME_BIOME=$(time -p biome check --apply > /dev/null 2>&1 && echo $?) TIME_ESLINT=$(time -p eslint . --fix > /dev/null 2>&1 && echo $?) TIME_PRETTIER=$(time -p prettier --write . > /dev/null 2>&1 && echo $?) # Sum ESLint+Prettier TIME_ESLINT_PRETTIER=$((TIME_ESLINT + TIME_PRETTIER)) echo "Biome: $TIME_BIOME, ESLint+Prettier: $TIME_ESLINT_PRETTIER"On a typical 200‑file project, Biome often finishes in ~5 s versus ~20 s for the two‑tool combo.
- Integrate into CI
# GitHub Actions example - name: Check code quality run: biome check --apply continue-on-error: falseReplace the ESLint/Prettier steps with a single Biome step. Add a lint‑only step if you want to fail the build on rule violations without applying fixes.
3. Trade‑offs & Limitations
- Plugin Ecosystem – Biome does not yet support custom ESLint plugins that rely on complex AST transformations or runtime evaluation. If your project uses
eslint-plugin-reactoreslint-plugin-import, you may need to keep a minimal ESLint pass for those rules. - Experimental Syntax – Features like optional chaining on classes or top‑level await are supported, but newer proposals (e.g., class static initialization blocks) may lag behind the official TypeScript compiler. Keep
tsconfig.jsonin sync and runtsc --noEmitalongside Biome to catch any TypeScript‑only errors. - Formatting Differences – Biome’s formatting algorithm is deterministic but not a drop‑in replacement for Prettier. In rare cases, code that Prettier formats identically may differ slightly in whitespace or line breaks. Run a diff after the first
biome check --applyto spot any surprises. - Learning Curve – Because Biome bundles linting and formatting, some ESLint rule semantics change. Review the rule list to map old rules to new ones.
4. Practical Checklist for a Smooth Transition
| Task | How to Verify |
|---|---|
| Install Biome | Run biome --version – should output a semantic version. |
| Generate config | Check that biome.json exists and contains "lint": {"rules": {}}. |
| Apply fixes | Run biome check --apply and ensure no files are modified after a second run. |
| Benchmark | Compare time biome check --apply vs time eslint . --fix && time prettier --write . – aim for a 50‑70 % reduction. |
| CI integration | Commit biome.json and CI step. Verify that a PR with a formatting error fails the build. |
| Fallback for unsupported rules | If a rule fails, add a minimal eslint step that runs only that rule and exits on error. |
Actionable Closing
Start small: run Biome locally on a feature branch, commit the changes, and add a single biome check --apply step to your CI. Monitor the runtime and diff output. If you hit a rule you can’t replace, keep a lightweight ESLint shim for that rule only. Over time, you’ll see your pipeline shrink, your config file disappear, and your developers spend less time wrestling with formatting mismatches.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.