Biome TypeScript Linting Skill
Routing Guide
| Task | Section |
| --------------------------------- | ------------------------------------------------- |
| Install or update Biome | Version Discovery |
| Initial setup | Quick Start |
| Configure rules | Configuration Patterns |
| Include/exclude files | File Targeting |
| Fix specific lint errors | See references/lint-solutions.md |
| Stricter rules beyond recommended | See references/strict-rules.md |
| Migrate from ESLint/Prettier | See references/migration.md |
| CI and git hooks | See references/ci-hooks.md |
Version Discovery
Never trust cached knowledge of Biome versions. Query the npm registry:
npm view @biomejs/biome version
Or for all recent versions:
npm view @biomejs/biome versions --json | tail -20
Check release notes for breaking changes:
curl -s https://api.github.com/repos/biomejs/biome/releases/latest | jq -r '.tag_name, .html_url'
Quick Start
Install as dev dependency, pinned to the release these examples target:
npm install --save-dev --save-exact @biomejs/biome@1.9.4
npx biome init
Pin 1.9.4 while following this skill. The $schema URL and the
files.include / files.ignore settings below are 1.9.4 spellings: Biome 2.0
replaced both keys with files.includes and moved organizeImports under
assist.actions.source.organizeImports. $schema must always match the
installed version, so run biome migrate --write and update $schema in the
same commit as any upgrade of Biome itself; see
Version Discovery.
This creates biome.json. Immediately verify:
npx biome check .
Minimal biome.json
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
}
}
Critical: Update the $schema URL to match your installed version.
Configuration Patterns
Rule Severity Levels
{
"linter": {
"rules": {
"recommended": true,
"suspicious": {
"noExplicitAny": "error",
"noArrayIndexKey": "warn"
},
"style": {
"noNonNullAssertion": "off"
}
}
}
}
Levels: "error" | "warn" | "off"
Rules with Options
Some rules accept configuration objects:
{
"linter": {
"rules": {
"style": {
"useNamingConvention": {
"level": "error",
"options": {
"strictCase": false,
"conventions": [
{
"selector": { "kind": "variable" },
"formats": ["camelCase", "CONSTANT_CASE"]
}
]
}
}
},
"complexity": {
"noExcessiveCognitiveComplexity": {
"level": "error",
"options": {
"maxAllowedComplexity": 15
}
}
}
}
}
}
Extending Configurations
{
"extends": ["./biome.base.json"]
}
Arrays merge; later entries override earlier ones.
File Targeting (Rough Edges)
Global Include/Exclude
Top-level files applies to all tools (linter, formatter, organizeImports):
{
"files": {
"include": ["src/**", "tests/**"],
"ignore": ["**/generated/**", "**/*.d.ts", "**/dist/**"]
}
}
Gotcha: Patterns are relative to biome.json location. Use **/ prefix
for recursive matching.
Tool-Specific Include/Exclude
Each tool can have its own file scope:
{
"linter": {
"include": ["src/**"],
"ignore": ["src/generated/**"]
},
"formatter": {
"include": ["src/**", "scripts/**"],
"ignore": ["src/vendor/**"]
}
}
Per-File Rule Overrides
Apply different rules to specific file patterns:
{
"overrides": [
{
"include": ["**/*.test.ts", "**/*.spec.ts"],
"linter": {
"rules": {
"suspicious": {
"noExplicitAny": "off"
}
}
}
},
{
"include": ["scripts/**"],
"linter": {
"rules": {
"suspicious": {
"noConsole": "off"
}
}
}
},
{
"include": ["**/*.config.ts", "**/*.config.js"],
"linter": {
"rules": {
"style": {
"noDefaultExport": "off"
}
}
}
}
]
}
Critical ordering: Overrides apply in array order. Later overrides win for the same file.
VCS Integration
Let Biome respect .gitignore:
{
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true,
"defaultBranch": "main"
}
}
With useIgnoreFile: true, anything in .gitignore is automatically excluded.
Command Reference
# Check without modifying (CI mode)
npx biome check .
# Fix all auto-fixable issues
npx biome check --write .
# Lint only (no formatting)
npx biome lint .
# Format only
npx biome format --write .
# Organize imports only
npx biome check --organize-imports-enabled=true --write .
# Check specific files
npx biome check src/index.ts src/utils/**/*.ts
# Output as JSON (for tooling)
npx biome check --reporter=json .
# Show which files would be processed
npx biome check --files-ignore-unknown=true --no-errors-on-unmatched .
Useful Flags
| Flag | Purpose |
| -------------------------- | ---------------------------------------------- |
| --write | Apply fixes |
| --unsafe | Apply unsafe fixes (review carefully) |
| --staged | Only check git-staged files |
| --changed | Only check files changed since default branch |
| --reporter=json | Machine-readable output |
| --diagnostic-level=error | Exit non-zero only on errors (ignore warnings) |
Suppression Comments
Suppress specific rules inline:
// biome-ignore lint/suspicious/noExplicitAny: external API requires any
const response: any = await legacyApi.fetch();
// biome-ignore lint/style/noNonNullAssertion: checked above
const element = document.getElementById("root")!;
Always include a reason after the colon. Biome enforces this—reasonless suppressions fail.
Avoid these lazy patterns:
// BAD: Will need revisiting
// biome-ignore lint/suspicious/noExplicitAny: TODO fix later
// biome-ignore lint/suspicious/noExplicitAny: too complex to type
// GOOD: Explains why suppression is necessary
// biome-ignore lint/suspicious/noExplicitAny: FFI boundary with untyped C library
// biome-ignore lint/suspicious/noExplicitAny: generic deserializer, caller provides type
Package.json Scripts
{
"scripts": {
"lint": "biome check .",
"lint:fix": "biome check --write .",
"format": "biome format --write .",
"check": "biome check --write --unsafe ."
}
}
TypeScript Integration
Biome only reads tsconfig.json for import path resolution when the nearest
tsconfig.json can supply compilerOptions.baseUrl or
compilerOptions.paths. Support for both starts in Biome 2.3. The examples
on this page pin 1.9.4, where an alias such as @app/foo is not resolved —
use relative imports, or upgrade to 2.3 or newer before relying on an alias:
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@app/*": ["./app/*"]
}
}
}
With Biome 2.3 or newer, the nearest tsconfig.json is picked up without any
Biome-side setting: baseUrl alone resolves bare specifiers from that directory
(import { foo } from "foo" finds src/foo.ts), and paths adds explicit
prefix mappings on top of it.
JSX and globals configuration live under javascript and are independent of
path resolution. The classic JSX runtime needs React declared as a global
because it emits React.createElement calls:
{
"javascript": {
"jsxRuntime": "reactClassic",
"globals": ["React"]
}
}
For JSX with the automatic runtime, no global is required:
{
"javascript": {
"jsxRuntime": "automatic"
}
}
When to Consult Reference Files
- Struggling with a specific lint error? →
references/lint-solutions.md - Want stricter rules than "recommended"? →
references/strict-rules.md - Migrating from ESLint or Prettier? →
references/migration.md - Setting up CI or git hooks? →
references/ci-hooks.md