Hi folks,
Welcome again! I hope you are doing well. I am thrilled to see you here. If you caught my first blog on Biome.js and actually tried it out, you felt that instant speed boost when switching from ESLint + Prettier. But here’s the real talk: the intro magic only gets you so far. Custom configs? That’s where Biome goes from “nice tool” to “can’t live without it.”
So today, we will discuss about the configuration you need when you use Biome.js.
Configuring Biome for Vanilla JS/TS Projects: The Basic Foundation
Let’s start simple:
No frameworks needed, just a Node.js CLI tool or vanilla Vite app.
The biome.json file lives in root directory and controls everything.
Think of it as your project’s DNA.
Below is the config for a TypeScript CLI tool:
{ "$schema": "https://biomejs.dev/schemas/1.9.6/schema.json", "formatter": { "enabled": true, "indentStyle": "space", "indentWidth": 2, "lineWidth": 100, "ignore": ["dist/**"] }, "linter": { "enabled": true, "rules": { "recommended": true, "correctness": { "noUndeclaredVariables": "error", "noUnusedVariables": "warn" }, "style": { "useFlatMap": "error", "noUselessCatch": "warn", "noDefaultExport": "warn" }, "suspicious": { "noExplicitAny": "warn" } } }, "javascript": { "formatter": { "quoteStyle": "single", "semicolons": "asNeeded", "trailingComma": "es5" }, "parser": { "unsafeParameterDecoratorsEnabled": true } }, "organizeImports": { "enabled": true }}
Let’s break down why each setting matters:
$schema: VS Code/WebStorm autocomplete + prevents schema drift as Biome evolves.lineWidth: 100is the suitable balance readability with modern wide screens (80 for React purists).useFlatMap: My favorite—catches.map(item => item).flat()and rewrites as.flatMap(item => item)noUselessCatch: Flagstry { ... } catch(e) {}blocks you forgot to handlequoteStyle: "single": Personal preference, but consistent across teams.organizeImports: Alphabetizes + groups your imports.
For vanilla JS projects, remove the javascript.parser section. Test with:
npx @biomejs/biome@latest check src/
Migrating Existing React & Angular Projects
Swapping tools in production code feels like defusing a bomb. I just survived two migrations and learned the hard way.
React Migration (Next.js/Vite/Create React App)
Step-by-step from my React 19 + Apollo project:
Install without removing old tools (safety first):
npm i -D @biomejs/biome
Port ESLint rules (most map 1:1):
npx @biomejs/biome migrate eslint
Rewrite package.json scripts:
{"scripts": {"lint": "biome lint --apply .","format": "biome format --write .","check": "biome check . --reporter=github","lint:staged": "biome lint --apply --files-ignore=\"dist/**\""}}
React/Next.js specific biome.json:
{ "linter": { "rules": { "recommended": true, "suspicious": { "noExplicitAny": "warn", "noArrayIndexKey": "error" }, "style": { "useHookAtTopLevel": "error", "useValidForDirection": "error" } } }, "overrides": [ { "include": ["src/**/*.tsx"], "options": { "parser": { "unsafeParameterDecoratorsEnabled": true } } } ]}
Angular 18 Migration (CLI + Standalone + Signals)
Angular’s stricter by nature, so expect more fixes:
{ "files": { "ignore": [ "dist/**", "node_modules/**", "**/*.angular-cli.json", "src/environments/*" ] }, "overrides": [ { "include": ["src/app/**/*.ts"], "linter": { "rules": { "correctness": { "noUndeclaredVariables": "off" }, "style": { "noDefaultExport": "off" } } } }, { "include": ["src/**/*.html"], "formatter": { "enabled": false } } ]}
Angular-specific wins:
1) Catches signal misuse before runtime errors
2) Standalone component imports get auto-sorted
3) Nx workspaces need files.maxSize: 1000000 for large files
Migration checklist (what saved me):
1. git commit -m "pre-biome" (rollback ready)
2. biome check src/ --reporter=json > biome-issues.json (baseline)
3. biome lint --apply src/ (fix safe issues)
4. biome format --write src/ (format everything)
5. Team review PR with only Biome changes
New React & Angular Projects: Start Clean
New React (Vite + TypeScript):
npm create vite@latest my-react-app — –template react-ts
cd my-react-app
npm i -D @biomejs/biome
npx @biomejs/biome@latest init
Enhanced biome.json:
{ "files": { "ignore": ["dist/", "node_modules/", "*.config.*"], "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.json"] }, "formatter": { "enabled": true }, "linter": { "rules": { "recommended": true, "style": { "useFlatMap": "error", "noDefaultExport": "warn", "noUselessCatch": "error" }, "suspicious": { "noExplicitAny": "error" } } }, "organizeImports": { "enabled": true }}
New Angular 18 (Standalone + Signals):
ng new my-angular-app --strict --standalone --routing=falsecd my-angular-appnpm i -D @biomejs/biomenpx @biomejs/biome@latest init
Angular starter config:
{ "javascript": { "parse": { "unsafeParameterDecoratorsEnabled": true, "allowSuperOutsideMethod": true } }, "overrides": [ { "include": ["src/app/**/*.component.ts"], "linter": { "rules": { "style": { "noDefaultExport": "off" } } } } ]}
Ignoring Linting & Formatting: Graceful Opt-Outs
Biome’s opinionated, but flexible. Here’s every way to say “not here, not now.”
1. Inline Comments (Most Common)
// biome-ignore lint/style/useFlatMap: third-party API limitation// biome-ignore lint/suspicious/noExplicitAny: legacy prop typeconst items = dangerousApi().map(x => x).flat();// biome-ignore format: preserve exact indentation for markdown parserconst markdown = ` ## Heading * Bullet stays indented`;
2. Rule Overrides in biome.json
{ "linter": { "rules": { "style": { "useFlatMap": "off", // Legacy codebase "noDefaultExport": "off" // Angular barrel files }, "suspicious": { "noExplicitAny": "warn" // Migration phase } } }}
3. File/Path Ignores
{"files": {"ignore": ["dist/", "build/","node_modules/.cache/", "legacy/",".min.js", ".generated.*"],"maxSize": 1000000 // 1MB limit for huge files}}
4. Dedicated .biomeignore
# Auto-generateddist/coverage/# Vendor + build toolsnode_modules/.cache/**/*.config.js# Legacy migration folderlegacy/
The Payoff: Real Numbers
| Project | Old Setup | Biome | Improvements |
|---|---|---|---|
| Angular Monorepo (50k LOC) | ESLint + Prettier: 2m 15s | 12s | 11x faster |
| React Vite (10k LOC) | ESLint + Prettier: 28s | 3s | 9x faster |
| Node CLI (5k LOC) | ESLint only: 12s | 1s | 12x faster |
Conclusion
Biome configs aren’t set-it-and-forget-it—they evolve with your project.
That Angular monorepo now runs linting in parallel across 15 apps.
React side project? Zero lint debt since day one.
Your Aim: Pick one project today. Run npx @biomejs/biome@latest init, commit the config, time your biome check .. Share your before/after numbers below!
Hey, let’s stay in touch!
If you liked this blog, please share it with your friends and colleagues. Connect with FE competency on LinkedIn to read more about such topics.