Biome and Ultracite
One tool for linting and formatting, a strict preset on top, and overrides that each have a reason.
Biome lints and formats JavaScript, TypeScript, JSON and CSS in one fast binary, so there's no ESLint plus Prettier plus plugins to keep in sync. Ultracite is a strict, ready-made Biome preset with React and Next.js rules, so I don't build the rule list by hand.
Setup
Install
pnpm add -D @biomejs/biome ultraciteExtend the presets
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"extends": [
"ultracite/biome/core",
"ultracite/biome/react",
"ultracite/biome/next"
]
}Point $schema at the installed package, not a URL, so the editor
autocompletes exactly the version you have.
Add the scripts
{
"scripts": {
"lint": "ultracite check",
"format": "ultracite fix",
"typecheck": "tsc --noEmit"
}
}Run pnpm lint and pnpm typecheck before every commit.
Overrides, each with a reason
A preset is a starting point. When a rule fights a decision the project has already made, turn it off and write down why, in the config itself.
Handlers don't need stable references
"linter": {
"rules": {
"performance": {
// Avoid unnecessary useCallback. Handlers passed to DOM elements
// gain nothing from stable references, so this rule is off.
"noJsxPropsBind": "off"
}
}
}PascalCase only where the convention says so
Component files are PascalCase, but UI primitives, hooks and utilities stay kebab-case. A scoped override enforces that only in the folders where it applies:
"overrides": [
{
"includes": [
"src/components/shared/**/*.tsx",
"src/features/*/components/**/*.tsx"
],
"linter": {
"rules": {
"style": {
"useFilenamingConvention": {
"level": "error",
"options": { "filenameCases": ["PascalCase"], "requireAscii": true }
}
}
}
}
}
]Tailwind v3 directives in CSS
Biome doesn't know @tailwind, so it reports it as an unknown at-rule.
Allow it in the one file that uses it:
{
"includes": ["src/styles/globals.css"],
"linter": {
"rules": {
"suspicious": {
"noUnknownAtRules": {
"level": "error",
"options": { "ignore": ["tailwind"] }
}
}
}
}
}Don't lint vendored code
Folders that a tool rewrites on every update (for example installed agent
skills) aren't your code. Exclude them with !! so Biome skips them entirely:
"files": {
"includes": ["!!.claude/skills", "!!.cursor/skills"]
}Fix on save, for people and AI editors
Formatting should never be a review comment. In my projects, Biome fixes run automatically:
- In the editor, through the Biome extension with format on save.
- After an AI edit, through a hook. Cursor runs it from
.cursor/hooks.jsonand Claude Code from.claude/settings.json, both callingpnpm dlx ultracite fix, so generated code lands already formatted.
Share rules with AI editors
The project's conventions live in .cursor/rules/*.mdc, and CLAUDE.md
points at the same files. One set of rules, followed by both tools.
Last updated on