Kim BoenderDitch ESLint and Prettier: A Practical Biome Migration Guide
Kim Boender
If you've ever spent 20 minutes debugging a mysterious ESLint config conflict, or watched your CI pipeline crawl through linting 10,000 files, you already know the problem I'm talking about. The JavaScript tooling ecosystem is famously fragmented, and keeping ESLint, Prettier, and all their shared plugins in sync is a tax every project eventually pays.
Biome is the most serious answer to that problem I've seen. It's a single Rust-based binary that replaces both ESLint and Prettier, and it's not just marginally faster, it's orders of magnitude faster. I've been running it on several of my TypeScript projects over the last few months, and I want to give you an honest picture of where it shines, where it still falls short (spoiler: Vue SFC support), and exactly how to migrate.
What Is Biome, Really?
Biome started as Rome Tools, a project that aimed to unify the entire JS toolchain under one roof. After Rome stalled, the community forked it and relaunched it as Biome under new stewardship. As of early 2026 it's at v2.x, ships over 400 lint rules, and has hit production-grade stability for TypeScript and JavaScript projects.
The pitch is simple: one config file, one CLI, one dependency, and performance that makes the Node.js tooling feel like it's running on a potato.
The Numbers
I'm not going to cherry-pick benchmarks, these are consistent across multiple sources:
- Linting 10,000 files: ESLint ~45s → Biome ~0.8s
- Formatting 10,000 files: Prettier ~12s → Biome ~0.3s
On a real-world project with a few hundred files, the difference is still dramatic. Cold linting a mid-size NestJS codebase that used to take 8-10 seconds now takes under a second. That adds up fast in a CI/CD pipeline.
Getting Started
Install Biome as a dev dependency, no global installs needed:
npm install --save-dev --save-exact @biomejs/biomeThe --save-exact flag is intentional. Biome's team recommends pinning the version because formatting output is part of its API surface, you don't want unexpected formatting changes sneaking in from a minor update.
Initialize your config:
npx @biomejs/biome initThis creates a biome.json in your project root.
Configuring biome.json
Here's a solid starting config for a TypeScript project:
{
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"noUnusedVariables": "error",
"noUnusedImports": "error"
},
"style": {
"noNonNullAssertion": "warn",
"useConst": "error"
},
"suspicious": {
"noExplicitAny": "warn"
}
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"trailingCommas": "all",
"semicolons": "asNeeded"
}
},
"files": {
"ignore": [
"node_modules",
"dist",
".nuxt",
".output",
"coverage"
]
}
}The organizeImports setting is a nice bonus, Biome will automatically sort and clean up your imports on format, something you'd previously need a separate ESLint plugin for.
Migrating from ESLint and Prettier
Biome ships a migration command that does a lot of the heavy lifting:
npx @biomejs/biome migrate eslint --write
npx @biomejs/biome migrate prettier --writeThese commands read your existing .eslintrc and .prettierrc configs and attempt to translate the settings into biome.json. In my experience, it handles about 80-90% of common rule configurations correctly. You'll still want to manually verify the output, especially if you've got custom plugins or complex overrides.
After migration, audit what rules didn't carry over:
npx @biomejs/biome migrate eslint 2>&1 | grep -i 'unsupported\|not found'Most "unsupported" rules are either already covered by Biome's built-in rule set under a different name, or they're things Biome intentionally doesn't have an equivalent for (usually the more opinionated formatting rules that Biome handles its own way).
Updating package.json Scripts
Swap out your old lint and format scripts:
{
"scripts": {
"lint": "biome lint .",
"lint:fix": "biome lint --write .",
"format": "biome format --write .",
"check": "biome check --write ."
}
}I prefer using biome check as the all-in-one command, it runs linting, formatting, and import organization in one pass. It's what I wire up to pre-commit hooks and CI.
VS Code Integration
Install the official Biome VS Code extension and add this to your workspace settings:
{
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"quickfix.biome": "explicit",
"source.organizeImports.biome": "explicit"
}
}Format on save just works, and the VS Code integration is genuinely solid at this point. No more fighting with eslint.validate arrays.
The Vue SFC Caveat, Read This Before Migrating
If you're primarily working on Vue projects (which, same), there's an important limitation: Biome doesn't fully support Vue Single File Components yet.
As of early 2026, .vue files have only partial support. Biome can format and lint the <script> and <script setup> blocks, but the HTML template and <style> blocks are not processed. That's a significant gap if your team relies on consistent template formatting.
The Hybrid Approach for Vue Projects
The pragmatic move for Vue projects right now is a hybrid setup:
- Use Biome for all your
.ts,.js, and pure TypeScript files (composables, stores, utilities, API layers) - Keep ESLint with
eslint-plugin-vuefor.vueSFC linting - Keep Prettier with
prettier-plugin-vuefor SFC formatting (or just format the script blocks with Biome and leave templates to Prettier)
Yes, it's not the clean single-tool story, but you still get Biome's speed on the bulk of your codebase. For a Nuxt 3 project, I configure Biome to only process server/, composables/, stores/, and utils/, and let ESLint handle the components/ and pages/ directories.
You can scope Biome's coverage in biome.json:
{
"files": {
"include": [
"src/**/*.ts",
"server/**/*.ts",
"composables/**/*.ts",
"stores/**/*.ts",
"utils/**/*.ts"
]
}
}Full Vue SFC support is actively being developed by the Biome team and is one of the most-requested features. Based on the pace of development, I expect meaningful progress in the second half of 2026.
CI/CD Integration
Biome is a single binary with no Node.js dependencies to resolve, which makes it genuinely fast in CI. A biome check run that includes linting and formatting in a GitHub Actions job:
- name: Biome check
run: npx @biomejs/biome ci .The ci command is the right choice for pipelines, it's like check but exits with a non-zero code on any violation and doesn't write fixes. If you're deploying to Railway or Vercel, trimming CI time down matters: fewer minutes billed, faster feedback loops.
Should You Migrate Right Now?
For pure TypeScript/Node.js projects, NestJS APIs, Express servers, CLI tools, shared utility packages, yes, migrate today. The speed improvement is real, the config is dramatically simpler, and the rule coverage is solid.
For Vue 3 / Nuxt 3 projects: partial migration makes sense. Use Biome for your TypeScript layers, keep ESLint+Prettier for SFCs, and plan to cut over fully once Vue SFC support lands. The hybrid approach is worth the setup time just for the CI speed gains.
What Biome gets right, beyond performance, is the philosophy: sensible defaults that work out of the box, zero-dependency install, and a config format that doesn't require a PhD in ESLint rule taxonomy. The JavaScript toolchain has needed this for years.