Upgrading Biome from 2.3 to 2.6 can fail in two ways. The config fails to load because two keys were removed, or biome check reports a pile of new errors on code that passed yesterday. Here's what the Biome 2.6 upgrade broke on a real Next.js 16 + Tailwind 4 + Playwright + Vitest codebase (this blog's), going from 2.3.8 to 2.6.6, and how to fix each one.
Quick answer
Run the migration first, then check:
pnpm add -D -E @biomejs/biome@2.6.6
pnpm biome migrate --write
pnpm biome check .
biome migrate handles most of the config changes. It renames useSortedClasses, moves its options to the new tailwind block, and replaces "recommended": true with "preset": "recommended". It does not remove javascript.parser.gritMetavariables, so delete that key by hand if you have it.
The new lint errors are mostly safe fixes:
pnpm biome check --write .
The config errors you see first
If your biome.json uses the old Tailwind sorting rule or the GritQL parser flag, Biome 2.6 refuses to run at all:
biome.json:8:9 deserialize
× Found an unknown key `useSortedClasses`.
> 8 │ "useSortedClasses": {
biome.json:20:7 deserialize
× Found an unknown key `gritMetavariables`.
> 20 │ "gritMetavariables": true
configuration
× Biome exited because the configuration resulted in errors. Please fix them.
Both keys are gone in 2.6. The nursery rule useSortedClasses is now useTailwindSortedClasses, and its options moved out of the rule into a top-level tailwind section. gritMetavariables was removed with no replacement.
Fix useSortedClasses
Before (works on 2.3):
{
"linter": {
"rules": {
"nursery": {
"useSortedClasses": {
"level": "warn",
"options": {
"attributes": ["className", "classList"],
"functions": ["clsx", "cn"]
}
}
}
}
}
}
After (2.6):
{
"linter": {
"rules": {
"nursery": {
"useTailwindSortedClasses": "warn"
}
}
},
"tailwind": {
"attributes": ["class", "className", "classList"],
"mergeFunctions": ["clsx", "cn"],
"variantFunctions": ["cva"]
}
}
The old functions option is now mergeFunctions. useTailwindSortedClasses itself takes no options anymore. If you don't set tailwind.attributes, the default is class and className. The default mergeFunctions list already includes clsx, cn, tw, twMerge and twJoin, so you may not need that line at all.
biome migrate --write produces this for you. It also adds "class" to attributes.
The sort order on a Tailwind v4 project came out the same as 2.3's:
-<div className="p-4 flex text-sm bg-white mt-2 rounded-lg">
+<div className="mt-2 flex rounded-lg bg-white p-4 text-sm">
That fix is marked unsafe, so it only applies with biome lint --write --unsafe.
Fix gritMetavariables
Delete it:
"javascript": {
- "parser": { "gritMetavariables": true }
}
biome migrate reports "configuration successfully migrated" but leaves this key in place. Run it a second time and it says "no migration needed", while biome check still fails on the same unknown key.
The recommended deprecation
This one doesn't break anything yet. Since 2.5, Biome prints an info line for "recommended": true:
The use of the recommended field has been deprecated, and will removed in the next major version of Biome. Use preset instead.
Replace it with:
"rules": { "preset": "recommended" }
On this blog's repo, that was the only config change migrate made, along with bumping $schema.
New lint errors after the upgrade
With the config fixed, biome check . on 387 files went from 94 errors and 153 warnings on 2.3.8 to 136 errors and 155 warnings on 2.6.6. Installing each version in between shows where they came from, and none came from 2.6:
assist/source/organizeImports, 38 new errors, starting in 2.4.7. Biome now sorts the names insideexport { ... }lists as well as imports. Every shadcn/ui component file ends with one of those lists, so almost all of them showed up.lint/suspicious/noArrayIndexKey, 4 new errors, starting in 2.4.5. It now catches the index inside template-literal keys likekey={`${item.url}-${i}`}.lint/complexity/useOptionalChain, 2 new warnings, starting in 2.4.9. It now flags!post || !post.title.
The organizeImports diagnostic looks like this:
src/components/ui/card.tsx:84:1 assist/source/organizeImports FIXABLE
× Sort the exported names.
> 84 │ export {
> 85 │ Card,
It's a safe fix, so biome check --write . clears all 38. The noArrayIndexKey ones need a real key. Use the item's id or url without the index, if that's unique.
If you're coming from 2.3, expect most of the new errors to come from the 2.4 patches.
The rules 2.6 promoted from nursery
Biome 2.6 moved 59 rules out of nursery into stable groups. Some of them are recommended, so they're on as soon as you upgrade, as long as the matching dependency is in package.json (@playwright/test, react, vitest or jest). Biome turns rules on by domain based on those dependencies.
None of them fired on this blog's code, but they're active. A probe file with a missing await failed right away:
e2e/probe.spec.ts:6:5 lint/correctness/noPlaywrightMissingAwait FIXABLE
× Async matcher toBeVisible must be awaited or returned.
> 6 │ expect(page.getByRole('heading')).toBeVisible();
i Unsafe fix: Add await before the call.
These promoted rules report as errors:
correctness/noPlaywrightMissingAwait: unawaited async matchers liketoBeVisible()correctness/usePlaywrightValidDescribeCallback:test.describe(async () => ...)correctness/noComponentHookFactories: a component defined inside another functioncorrectness/noJsxNamespace:<svg:rect>style JSXcorrectness/useReactAsyncServerFunction: a non-async export in a"use server"file
These report as warnings:
suspicious/noReactStringRefs:ref="input"suspicious/noIdenticalTestTitle: two tests with the same title in one scope
A missing await on a Playwright matcher means the assertion is never checked, so the test can pass while the page is broken. That makes noPlaywrightMissingAwait a bad candidate for off.
biome explain <rule> shows the version the rule first appeared in nursery (2.4.2 for noPlaywrightMissingAwait), not 2.6. Don't use it to work out whether 2.6 changed anything.
Turn off a promoted rule
Set it to off in its new group, not in nursery:
"linter": {
"rules": {
"preset": "recommended",
"suspicious": { "noIdenticalTestTitle": "off" }
}
}
If you had already enabled one of these in nursery on 2.3, that also errors with Found an unknown key. biome migrate moves it to the right group and leaves an empty "nursery": {} behind.
To disable a whole domain:
"linter": { "domains": { "playwright": "none" } }
To skip a rule for one run, for example while you fix the backlog:
pnpm biome check --skip=correctness/usePlaywrightValidDescribeCallback .
Tailwind sorting is still opt-in
The 2.6 release notes say the Tailwind domain auto-enables for tailwindcss v4+. In practice nothing changed. Every Tailwind rule is still in nursery, and domains never turn on nursery rules. With Tailwind 4.1, the recommended preset, and even "domains": { "tailwind": "all" }, Biome reported zero Tailwind diagnostics.
If you want class sorting, enable useTailwindSortedClasses yourself, as in the config above. It works on Tailwind 3 too. Its default severity is info, so set "warn" or "error" if you want CI to notice.
Verify it works
pnpm biome --version
pnpm biome check .
You should see Version: 2.6.6 and no deserialize errors at the top of the output. If check still exits 1, look at the rule names. organizeImports and format are safe to --write. Anything under correctness deserves a look first.
Conclusion
For a Biome 2.6 upgrade from 2.3, run biome migrate --write, delete gritMetavariables by hand, then biome check --write . to clear the export-sorting errors from 2.4. The promoted Playwright and React rules only fire on real mistakes, so read those before you switch them off.