Biome es una cadena de herramientas ultra rápida para JavaScript/TypeScript escrita en Rust. Integra las funciones de ESLint y Prettier en una sola herramienta, mejorando la experiencia de desarrollo con una velocidad abrumadora. En 2025, con el lanzamiento de Biome 2.0, su adopción está avanzando en más proyectos.
¿Qué es Biome?
Comparación de cadenas de herramientas
flowchart TB
subgraph Traditional["Configuración tradicional"]
Workflow1["Flujo de desarrollo"]
ESLint["ESLint<br/>(JavaScript)"]
Prettier["Prettier<br/>(JavaScript)"]
TSC["TypeScript<br/>tsc"]
Config1["Se necesitan múltiples archivos de configuración<br/>(.eslintrc, .prettierrc, tsconfig.json)"]
Workflow1 --> ESLint
Workflow1 --> Prettier
Workflow1 --> TSC
ESLint --> Config1
Prettier --> Config1
TSC --> Config1
end
flowchart TB
subgraph BiomeArch["Configuración de Biome"]
Workflow2["Flujo de desarrollo"]
Biome["Biome (Rust)<br/>✓ Linter<br/>✓ Formatter<br/>✓ Import Sort"]
Config2["Gestión con 1 archivo biome.json"]
Workflow2 --> Biome --> Config2
end
Comparación de rendimiento
| Herramienta | Procesamiento de 1000 archivos | Uso de memoria |
|---|---|---|
| ESLint + Prettier | Aprox. 30s | ~500MB |
| Biome | Aprox. 0.5s | ~50MB |
Aproximadamente 60 veces más rápido, 1/10 de memoria
Instalación y configuración
Instalación básica
# 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
# Inicialización del archivo de configuración
npx @biomejs/biome init
Archivo de configuración básico
// 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"
}
}
}
Configuración de scripts en package.json
{
"scripts": {
"lint": "biome lint .",
"lint:fix": "biome lint --write .",
"format": "biome format --write .",
"check": "biome check .",
"check:fix": "biome check --write .",
"ci": "biome ci ."
}
}
Configuración detallada
Ejemplo de configuración completa
{
"$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"
}
}
}
}
]
}
Categorías de reglas del Linter
Lista de categorías de reglas
| Categoría | Ejemplo de regla | Descripción |
|---|---|---|
| a11y (Accesibilidad) | useAltText | Requiere atributo alt en imágenes |
| useKeyWithClickEvents | Operación de teclado para eventos de clic | |
| complexity (Complejidad) | noBannedTypes | Detecta uso de tipos prohibidos |
| noForEach | Recomienda forEach → for…of | |
| noExcessiveCognitiveComplexity | Límite de complejidad cognitiva | |
| correctness (Corrección) | noUnusedVariables | Detección de variables no usadas |
| noUnusedImports | Detección de imports no usados | |
| useExhaustiveDependencies | Array de dependencias de useEffect | |
| performance (Rendimiento) | noAccumulatingSpread | Prohibir acumulación de spread |
| noDelete | Advertir uso de operador delete | |
| security (Seguridad) | noDangerouslySetInnerHtml | Detección de vulnerabilidades XSS |
| noGlobalEval | Prohibir uso de eval() | |
| style (Estilo) | useConst | Usar const para variables sin reasignación |
| useTemplate | Template literal sobre concatenación de strings | |
| noParameterAssign | Prohibir reasignación a parámetros | |
| suspicious (Código sospechoso) | noExplicitAny | Advertir uso de tipo any |
| noConsoleLog | Advertir uso de console.log | |
| noDebugger | Prohibir sentencias debugger |
Ejemplo de configuración detallada de reglas
{
"linter": {
"rules": {
"correctness": {
"useExhaustiveDependencies": {
"level": "warn",
"options": {
"hooks": [
{
"name": "useQuery",
"stableResult": [1]
},
{
"name": "useMutation",
"stableResult": [1]
},
{
"name": "useCustomHook",
"closureIndex": 0,
"dependenciesIndex": 1
}
]
}
}
}
}
}
}
Migración desde ESLint/Prettier
Lista de verificación de migración
# 1. Instalar Biome
npm install --save-dev @biomejs/biome
# 2. Inicializar archivo de configuración
npx @biomejs/biome init
# 3. Migración desde configuración ESLint (automática)
npx @biomejs/biome migrate eslint --write
# 4. Migración desde configuración Prettier (automática)
npx @biomejs/biome migrate prettier --write
Tabla de correspondencia de reglas ESLint
| ESLint | Biome | Categoría |
|---|---|---|
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 |
Estrategia de migración gradual
// biome.json - Configuración para migración gradual
{
"linter": {
"enabled": true,
"rules": {
"recommended": true,
// Inicialmente solo advertencias
"correctness": {
"noUnusedVariables": "warn",
"noUnusedImports": "warn"
},
"suspicious": {
"noExplicitAny": "warn",
"noConsoleLog": "off" // Deshabilitado inicialmente
}
}
}
}
# Eliminar ESLint/Prettier después de la migración
npm uninstall eslint prettier eslint-config-prettier eslint-plugin-*
# Eliminar archivos de configuración
rm .eslintrc* .prettierrc* .eslintignore .prettierignore
Integración con editores
VS Code
// .vscode/extensions.json
{
"recommendations": ["biomejs.biome"]
}
// .vscode/settings.json
{
// Biome como formateador predeterminado
"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-formatear al guardar
"editor.formatOnSave": true,
// Ejecutar acciones de código al guardar
"editor.codeActionsOnSave": {
"quickfix.biome": "explicit",
"source.organizeImports.biome": "explicit"
},
// Deshabilitar 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,
})
-- Integración con 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.)
Pasos de configuración:
1. Settings → Languages & Frameworks → JavaScript → Code Quality Tools → Biome
2. Marcar "Enable"
3. Biome package: node_modules/@biomejs/biome
4. Configuration file: biome.json
Configuración de formato:
1. Settings → Editor → Code Style
2. Desactivar "Enable EditorConfig support"
3. Settings → Tools → Actions on Save
4. Marcar "Reformat code" y "Run Biome"
Integración CI/CD
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 .
# Agregar anotaciones inline al 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"
Hook pre-commit (Husky + lint-staged)
# Instalar 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
Uso avanzado
API de plugins (función experimental)
// biome-plugin.js - Ejemplo de regla personalizada
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',
});
}
},
};
},
},
},
};
Configuración de Monorepo
// biome.json en la raíz
{
"$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 - Configuración específica del paquete
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"extends": ["../../biome.json"],
"linter": {
"rules": {
"correctness": {
"useExhaustiveDependencies": "warn"
}
}
}
}
Ordenamiento personalizado de imports
{
"javascript": {
"organizeImports": {
"groups": [
["react", "react-dom"],
["^@?\\w"],
["^@/"],
["^\\.\\."],
["^\\."]
]
}
}
}
Solución de problemas
Problemas comunes y soluciones
# Limpiar caché
rm -rf node_modules/.cache/biome
# Salida de error detallada
npx @biomejs/biome check --verbose .
# Depurar archivo específico
npx @biomejs/biome lint --verbose src/problematic-file.ts
# Validar configuración
npx @biomejs/biome check --config-path biome.json --verbose
Comentarios de supresión de errores
// Deshabilitar regla específica en todo el archivo
// biome-ignore-all lint/suspicious/noExplicitAny: Legacy code
// Deshabilitar solo la siguiente línea
// biome-ignore lint/suspicious/noExplicitAny: Necessary for this API
const data: any = response.data;
// Deshabilitar múltiples reglas
// biome-ignore lint/style/useConst lint/correctness/noUnusedVariables: Intentional
let unusedVar = 'test';
// Deshabilitar formato
// biome-ignore format: Keep this formatting
const matrix = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
];
Benchmark de rendimiento
Comparación en proyecto grande (10,000 archivos):
| Herramienta | Tiempo lint | Tiempo format | Uso memoria |
|---|---|---|---|
| ESLint | 120s | - | 1.2GB |
| Prettier | - | 45s | 800MB |
| ESLint+Prettier | 165s | (total) | 2.0GB |
| Biome | 2s | 1.5s | 150MB |
| Mejora | 60x más rápido | 30x más rápido | 13x menos |
Resumen
Biome mejora significativamente la cadena de herramientas de desarrollo frontend.
Beneficios de adoptar Biome
| Aspecto | ESLint+Prettier | Biome |
|---|---|---|
| Velocidad | Lenta | Ultra rápida (60x+) |
| Archivos de config | Múltiples | 1 archivo |
| Dependencias | Muchas | 1 |
| Uso de memoria | Alto | Bajo |
| Consistencia de config | Posible conflicto | Integrada |
Casos de uso recomendados
- Nuevos proyectos
- Proyectos que necesitan acelerar CI
- Equipos que buscan simplificar configuración
- Entornos Monorepo
Biome se está convirtiendo en el nuevo estándar del ecosistema JavaScript.