Biome is an ultra-fast JavaScript/TypeScript toolchain written in Rust. It integrates the functionality of ESLint and Prettier into one tool, improving the development experience with overwhelming speed. In 2025, with the release of Biome 2.0, adoption in more projects is progressing.
What is Biome
Toolchain Comparison
flowchart TB
subgraph Traditional["Traditional setup"]
Workflow1["Development Workflow"]
ESLint["ESLint<br/>(JavaScript)"]
Prettier["Prettier<br/>(JavaScript)"]
TSC["TypeScript<br/>tsc"]
Config1["Multiple config files needed<br/>(.eslintrc, .prettierrc, tsconfig.json)"]
Workflow1 --> ESLint
Workflow1 --> Prettier
Workflow1 --> TSC
ESLint --> Config1
Prettier --> Config1
TSC --> Config1
end
flowchart TB
subgraph BiomeArch["Biome setup"]
Workflow2["Development Workflow"]
Biome["Biome (Rust)<br/>✓ Linter<br/>✓ Formatter<br/>✓ Import Sort"]
Config2["Managed with single biome.json"]
Workflow2 --> Biome --> Config2
end
Performance Comparison
| Tool | 1000 File Processing | Memory Usage |
|---|---|---|
| ESLint + Prettier | ~30 seconds | ~500MB |
| Biome | ~0.5 seconds | ~50MB |
About 60x faster, 1/10 memory
Installation and Setup
Basic Installation
# npm
npm install --save-dev --save-exact @biomejs/biome
# pnpm
pnpm add --save-dev --save-exact @biomejs/biome
# yarn
yarn add --dev --exact @biomejs/biome
# bun
bun add --dev --exact @biomejs/biome
# Initialize config file
npx @biomejs/biome init
Basic Configuration File
// biome.json
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"trailingCommas": "all",
"semicolons": "always"
}
}
}
package.json Script Configuration
{
"scripts": {
"lint": "biome lint .",
"lint:fix": "biome lint --write .",
"format": "biome format --write .",
"check": "biome check .",
"check:fix": "biome check --write .",
"ci": "biome ci ."
}
}
Detailed Configuration
Complete Configuration Example
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"files": {
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js", "src/**/*.jsx"],
"ignore": [
"node_modules",
"dist",
"build",
".next",
"coverage",
"*.min.js"
]
},
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"complexity": {
"noExcessiveCognitiveComplexity": {
"level": "warn",
"options": {
"maxAllowedComplexity": 15
}
},
"noForEach": "warn"
},
"correctness": {
"noUnusedVariables": "error",
"noUnusedImports": "error",
"useExhaustiveDependencies": "warn"
},
"performance": {
"noAccumulatingSpread": "warn",
"noDelete": "warn"
},
"security": {
"noDangerouslySetInnerHtml": "error"
},
"style": {
"noNonNullAssertion": "warn",
"useConst": "error",
"useTemplate": "error",
"noParameterAssign": "error"
},
"suspicious": {
"noExplicitAny": "warn",
"noConsoleLog": "warn",
"noDebugger": "error"
},
"nursery": {
"useSortedClasses": {
"level": "warn",
"options": {
"attributes": ["className", "class"],
"functions": ["clsx", "cn", "cva"]
}
}
}
}
},
"formatter": {
"enabled": true,
"formatWithErrors": false,
"indentStyle": "space",
"indentWidth": 2,
"lineEnding": "lf",
"lineWidth": 100,
"attributePosition": "auto"
},
"javascript": {
"formatter": {
"enabled": true,
"quoteStyle": "single",
"jsxQuoteStyle": "double",
"quoteProperties": "asNeeded",
"trailingCommas": "all",
"semicolons": "always",
"arrowParentheses": "always",
"bracketSpacing": true,
"bracketSameLine": false
},
"parser": {
"unsafeParameterDecoratorsEnabled": true
}
},
"json": {
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
}
},
"css": {
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100,
"quoteStyle": "double"
},
"linter": {
"enabled": true
}
},
"overrides": [
{
"include": ["*.test.ts", "*.test.tsx", "*.spec.ts"],
"linter": {
"rules": {
"suspicious": {
"noExplicitAny": "off"
}
}
}
},
{
"include": ["*.config.js", "*.config.ts"],
"linter": {
"rules": {
"style": {
"noDefaultExport": "off"
}
}
}
}
]
}
Linter Rule Categories
Rule Category List
| Category | Rule | Description |
|---|---|---|
| a11y (Accessibility) | useAltText | Require alt attribute on images |
| useKeyWithClickEvents | Key operation on click | |
| complexity (Complexity) | noBannedTypes | Detect use of banned types |
| noForEach | Recommend for…of over forEach | |
| noExcessiveCognitiveComplexity | Complexity limit | |
| correctness (Correctness) | noUnusedVariables | Detect unused variables |
| noUnusedImports | Detect unused imports | |
| useExhaustiveDependencies | useEffect deps array | |
| performance (Performance) | noAccumulatingSpread | Prohibit cumulative spread |
| noDelete | Warn on delete operator usage | |
| security (Security) | noDangerouslySetInnerHtml | Detect XSS vulnerability |
| noGlobalEval | Prohibit eval() usage | |
| style (Style) | useConst | Use const for non-reassigned variables |
| useTemplate | Template literals over concatenation | |
| noParameterAssign | Prohibit parameter reassignment | |
| suspicious (Suspicious Code) | noExplicitAny | Warn on any type usage |
| noConsoleLog | Warn on console.log usage | |
| noDebugger | Prohibit debugger statement |
Detailed Rule Configuration Example
{
"linter": {
"rules": {
"correctness": {
"useExhaustiveDependencies": {
"level": "warn",
"options": {
"hooks": [
{
"name": "useQuery",
"stableResult": [1]
},
{
"name": "useMutation",
"stableResult": [1]
},
{
"name": "useCustomHook",
"closureIndex": 0,
"dependenciesIndex": 1
}
]
}
}
}
}
}
}
Migration from ESLint/Prettier
Migration Checklist
# 1. Install Biome
npm install --save-dev @biomejs/biome
# 2. Initialize config file
npx @biomejs/biome init
# 3. Auto-migrate from ESLint config
npx @biomejs/biome migrate eslint --write
# 4. Auto-migrate from Prettier config
npx @biomejs/biome migrate prettier --write
ESLint Rule Mapping Table
| ESLint | Biome | Category |
|---|---|---|
no-unused-vars | noUnusedVariables | correctness |
no-console | noConsoleLog | suspicious |
eqeqeq | noDoubleEquals | suspicious |
prefer-const | useConst | style |
no-var | noVar | style |
@typescript-eslint/no-explicit-any | noExplicitAny | suspicious |
react-hooks/exhaustive-deps | useExhaustiveDependencies | correctness |
jsx-a11y/alt-text | useAltText | a11y |
Gradual Migration Strategy
// biome.json - Configuration for gradual migration
{
"linter": {
"enabled": true,
"rules": {
"recommended": true,
// Warnings only at first
"correctness": {
"noUnusedVariables": "warn",
"noUnusedImports": "warn"
},
"suspicious": {
"noExplicitAny": "warn",
"noConsoleLog": "off" // Disabled at first
}
}
}
}
# Remove ESLint/Prettier after migration
npm uninstall eslint prettier eslint-config-prettier eslint-plugin-*
# Delete config files
rm .eslintrc* .prettierrc* .eslintignore .prettierignore
Editor Integration
VS Code
// .vscode/extensions.json
{
"recommendations": ["biomejs.biome"]
}
// .vscode/settings.json
{
// Set Biome as default formatter
"editor.defaultFormatter": "biomejs.biome",
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
"editor.defaultFormatter": "biomejs.biome"
},
// Auto-format on save
"editor.formatOnSave": true,
// Run code actions on save
"editor.codeActionsOnSave": {
"quickfix.biome": "explicit",
"source.organizeImports.biome": "explicit"
},
// Disable ESLint/Prettier
"eslint.enable": false,
"prettier.enable": false
}
Neovim (nvim-lspconfig)
-- init.lua
require('lspconfig').biome.setup({
cmd = { 'npx', 'biome', 'lsp-proxy' },
root_dir = require('lspconfig.util').root_pattern('biome.json', 'biome.jsonc'),
single_file_support = false,
})
-- Integration with null-ls.nvim
local null_ls = require('null-ls')
null_ls.setup({
sources = {
null_ls.builtins.formatting.biome,
null_ls.builtins.diagnostics.biome,
},
})
JetBrains IDE (WebStorm, etc.)
Setup steps:
1. Settings → Languages & Frameworks → JavaScript → Code Quality Tools → Biome
2. Check "Enable"
3. Biome package: node_modules/@biomejs/biome
4. Configuration file: biome.json
Format settings:
1. Settings → Editor → Code Style
2. Turn off "Enable EditorConfig support"
3. Settings → Tools → Actions on Save
4. Check "Reformat code" and "Run Biome"
CI/CD Integration
GitHub Actions
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run Biome CI
run: npx @biomejs/biome ci .
# Add inline annotations to PR
lint-review:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Setup Biome
uses: biomejs/setup-biome@v2
with:
version: latest
- name: Run Biome with reviewdog
uses: reviewdog/action-biome@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
reporter: github-pr-review
GitLab CI
# .gitlab-ci.yml
stages:
- lint
biome:
stage: lint
image: node:20-alpine
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
script:
- npm ci
- npx @biomejs/biome ci .
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"
pre-commit Hook (Husky + lint-staged)
# Install Husky
npm install --save-dev husky lint-staged
npx husky init
// package.json
{
"lint-staged": {
"*.{js,jsx,ts,tsx,json,css}": [
"biome check --write --no-errors-on-unmatched"
]
}
}
# .husky/pre-commit
npx lint-staged
Advanced Usage
Plugin API (Experimental Feature)
// biome-plugin.js - Custom rule example
export default {
name: 'my-custom-rules',
rules: {
noTodoComments: {
meta: {
docs: {
description: 'Disallow TODO comments in production code',
},
},
create(context) {
return {
Comment(node) {
if (node.value.includes('TODO')) {
context.report({
node,
message: 'TODO comments should be resolved before merge',
});
}
},
};
},
},
},
};
Monorepo Configuration
// Root biome.json
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"extends": [],
"files": {
"ignore": ["packages/*/dist", "packages/*/node_modules"]
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"formatter": {
"enabled": true
}
}
// packages/web/biome.json - Package-specific config
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"extends": ["../../biome.json"],
"linter": {
"rules": {
"correctness": {
"useExhaustiveDependencies": "warn"
}
}
}
}
Custom Import Sorting
{
"javascript": {
"organizeImports": {
"groups": [
["react", "react-dom"],
["^@?\\w"],
["^@/"],
["^\\.\\."],
["^\\."]
]
}
}
}
Troubleshooting
Common Issues and Solutions
# Clear cache
rm -rf node_modules/.cache/biome
# Verbose error output
npx @biomejs/biome check --verbose .
# Debug specific file
npx @biomejs/biome lint --verbose src/problematic-file.ts
# Validate config
npx @biomejs/biome check --config-path biome.json --verbose
Error Suppression Comments
// Disable specific rule for entire file
// biome-ignore-all lint/suspicious/noExplicitAny: Legacy code
// Disable for next line only
// biome-ignore lint/suspicious/noExplicitAny: Necessary for this API
const data: any = response.data;
// Disable multiple rules
// biome-ignore lint/style/useConst lint/correctness/noUnusedVariables: Intentional
let unusedVar = 'test';
// Disable formatting
// biome-ignore format: Keep this formatting
const matrix = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
];
Performance Benchmark
Large project comparison (10,000 files):
| Tool | lint time | format time | Memory usage |
|---|---|---|---|
| ESLint | 120s | - | 1.2GB |
| Prettier | - | 45s | 800MB |
| ESLint+Prettier | 165s | (total) | 2.0GB |
| Biome | 2s | 1.5s | 150MB |
| Improvement | 60x faster | 30x faster | 13x less |
Summary
Biome significantly improves the frontend development toolchain.
Benefits of Adopting Biome
| Item | ESLint+Prettier | Biome |
|---|---|---|
| Speed | Slow | Ultra-fast (60x+) |
| Config files | Multiple | 1 file |
| Dependencies | Many | 1 |
| Memory usage | Large | Small |
| Config consistency | Potential conflicts | Integrated |
Recommended Adoption Cases
- New projects
- Projects needing CI speedup
- Teams seeking config simplification
- Monorepo environments
Biome is becoming the new standard in the JavaScript ecosystem.