Biome
Biome is the only formatter and linter for JS, TS, JSX, JSON and CSS. SCSS is the exception: Biome 2.5 doesn't parse it, so stylelint handles .scss (see scss-bem). No ESLint, no Prettier.
The template's root biome.json is the source of truth. A plugin hook runs pnpm exec biome check --write <file> after every file Claude edits, when a biome.json exists above it.
Rules
-
One root
biome.json, schema pinned to the installed version."$schema": "https://biomejs.dev/schemas/2.5.15/schema.json", and@biomejs/biomepinned to the same version in the workspace catalog. A mismatched schema hides unknown or renamed options in the editor. When upgrading, runpnpm exec biome migrate --writeand update both. -
Let git decide what's ignored:
vcs.useIgnoreFile: true. Then force-ignore build output with!!infiles.includes(!!**/.next,!!**/.turbo,!!**/dist,!!**/coverage), which also stops Biome from scanning those folders. A single!excludes from processing but still indexes.files.ignoreUnknown: truekeeps.scssand other unsupported files from being reported. -
Formatter: 2 spaces, width 100, LF, double quotes, semicolons, trailing commas. These are fixed for every repo so diffs don't fight over style. Don't add per-package formatter overrides.
-
Linter:
preset: "recommended",a11y: "error", domainsreactandnextatrecommended.rules.recommended: trueis the old spelling and is deprecated: userules.preset.a11yaterrorbecause nuvui handles its own components' accessibility, so what's left (alt, labels, button types in your own markup) must be right. Thenextdomain adds rules likenoImgElement;reactadds hooks rules. -
Nursery stays off. Nursery rules change between patch releases and can break CI on a Biome upgrade. Enable one deliberately, by name, with a comment saying why, and re-check on every upgrade.
-
noConsolewarns, allowingerrorandwarn. Structured error logs (instrumentation.ts) useconsole.error; strayconsole.logis caught. -
Sort imports with the assist, not by hand.
assist.actions.source.organizeImports: "on".biome check --writeapplies it;biome cifails on unsorted imports. -
Overrides by glob, for test files and the like. The template enables the
testdomain only for*.test.ts(x)/*.test-d.ts. Put other file-type differences there too, not in separate configs. -
Nested configs only for packages that truly differ, and they extend the root.
// packages/legacy-widget/biome.json { "extends": "//", "linter": { "rules": { "style": { "noNonNullAssertion": "off" } } } }"extends": "//"means "the root config" and implies"root": false. Any other nested config must set"root": false. We don't use a shared@repo/biome-configpackage: the root file plus//covers it without a build or a dependency. -
Locally
biome check --write; in CIbiome ci.pnpm lintrunsbiome check .(no writes);pnpm lint:fixrunsbiome check --write .. CI runspnpm exec biome ci --reporter=github ., which never writes, fails on formatting, lint and import order, and annotates the PR. Add--changed --since=origin/mainonly if the full run gets slow; it's milliseconds today. -
Suppress one line, with a reason.
// biome-ignore lint/suspicious/noExplicitAny: third-party callback typed as any upstreamNever disable a rule repo-wide to make one file pass. If a rule is wrong for the repo, change
biome.jsonin its own commit with the reason. -
Fix, don't silence, the common ones.
useImportType: run the fix; it's required byverbatimModuleSyntax.noExplicitAny: useunknownand parse (seetypescript-production).useButtonType,noSvgWithoutTitle,useAltText: real accessibility bugs.noImgElement: usenext/image.useExhaustiveDependencies: fix the dependency list or move the value out of the component.
-
VS Code: Biome is the default formatter, fixes on save. The template's
.vscode/settings.jsonsetseditor.defaultFormatter: "biomejs.biome",source.fixAll.biomeandsource.organizeImports.biomeon save, andbiome.requireConfiguration: trueso the extension stays quiet in repos without Biome. SCSS keeps the stylelint extension. Recommend both in.vscode/extensions.json. -
Migrating from ESLint/Prettier: migrate, then delete. See references/migration.md. End state: no
eslint*,prettier*or@typescript-eslint/*packages, no.eslintrc*,eslint.config.*,.prettierrc*,.prettierignore.
What Biome doesn't cover
| File | Tool |
|---|---|
.scss | stylelint (scss-bem) |
.yaml (workflows, Helm) | actionlint for workflows; helm lint + kubeconform for charts |
.md | nothing enforced |
| Type errors | tsc --noEmit (pnpm typecheck) |
Verify
pnpm lint # biome check . && stylelint
pnpm exec biome ci --reporter=github . # what CI runs
pnpm exec biome rage # prints the resolved config if something looks ignored
Sources
- https://biomejs.dev/reference/configuration/
- https://biomejs.dev/guides/big-projects/
- https://biomejs.dev/linter/domains/
- https://biomejs.dev/linter/rules/
- https://biomejs.dev/reference/cli/
- https://biomejs.dev/reference/vscode/
- https://biomejs.dev/guides/migrate-eslint-prettier/
- https://biomejs.dev/internals/language-support/