Biome 09 🧹 Rule Domains — Grouping rules by framework and environment
Biome’s rule domains are the technology-specific layer that sits on top of the problem-category groups. A domain is a named collection of rules that applies to a particular framework, library, or environment—React, Next.js, Solid, Vue, the test runners, Playwright, Tailwind, Turborepo, and the project-level analysis. The domain is not the same as the group. The group answers “what kind of problem is this?” and the domain answers “what technology is this code written for?” The useExhaustiveDependencies rule belongs to the correctness group and to the react domain simultaneously . The two classifications are orthogonal, and the domain is the mechanism that enables the framework-specific checks without forcing the project to carry the rules for a framework it does not use.
The domain system was introduced in Biome 2.0, and it changed the default recommended set from a fixed list to a project-aware list. Biome inspects the package.json and enables the domains whose dependencies are declared. A project with react in its dependencies gets the react domain enabled by default, and a project with vitest gets the test domain . The automatic detection is the mechanism that makes the default configuration relevant to the project without any explicit configuration. A project that wants a different set overrides the detection with the explicit linter.domains object.
The domain is configured with the linter.domains object. The key is the domain name, and the value is "all", "recommended", or "none" . The "recommended" enables the stable recommended rules of the domain, the "all" enables every rule including the nursery ones, and the "none" disables the domain explicitly. The domain and the group are the two axes of the configuration, and the project combines them to get the set of rules it wants.
Key point: Domains group rules by technology and are orthogonal to the correctness/suspicious/style groups. Biome auto-detects the domains from the package.json dependencies. The linter.domains object overrides the detection with "all", "recommended", or "none". The "recommended" mode enables the stable rules of the domain and skips the nursery rules, and the test domain also adds the test globals to the configuration.
Why the domains exist
The framework problem. A React project uses hooks, and the useExhaustiveDependencies rule catches the missing dependencies in the dependency array. A Solid project uses signals, and the React rule is not just noise—it is actively wrong, because the Solid reactivity model is different. The Biome documentation states that the react domain enables rules that “might conflict with the Solid domain,” and the solid domain enables rules that “might conflict with the React domain” . The domain is the mechanism that selects the correct framework rules, and the auto-detection from the package.json is the mechanism that makes the selection automatic.
The environment problem. A test file uses the test.only and the describe.skip, and the noFocusedTests rule catches the committed focus. A production file does not use the test APIs, and the rule is irrelevant. The test domain is the mechanism that enables the test rules and the test globals—describe, it, expect, beforeEach, and the others—only when the project uses a test runner . The domain adds the globals to the configuration, which means the project does not need to declare them manually.
The auto-detection problem. A project that adds Biome to a Next.js codebase and writes no configuration would get the React rules but not the Next.js rules, because the two are separate domains. The auto-detection reads the package.json and enables both the react and the next domains when the dependencies are present . The detection is the mechanism that makes the zero-config setup useful, and the explicit configuration is the override for the project that wants a different set.
The performance problem. The project domain is different from the framework domains. Its rules perform the cross-file analysis—the module graph, the dependency resolution, the unresolved imports—and enabling them makes Biome scan the entire project . The scan has a performance cost, and the domain is the mechanism that makes the cost opt-in. A project that does not need the cross-file analysis does not enable the domain, and the linting stays fast.
The nursery problem. A domain can contain the rules that are still in the nursery group, and the "recommended" mode enables only the stable rules. The test domain’s noFocusedTests and noExportsInTest are recommended, while the useConsistentTestIt and the useTestHooksInOrder are nursery . The distinction is the mechanism that lets the domain evolve without breaking the projects that enable it.
a. The domain configuration and the auto-detection
The linter.domains object configures the domains. The key is the domain name, and the value is "all", "recommended", or "none". The "recommended" enables the stable recommended rules, the "all" enables every rule including the nursery ones, and the "none" disables the domain explicitly .
// biome.json — the explicit domain configuration
{
"linter": {
"rules": { "recommended": true },
"domains": {
"react": "recommended",
"test": "all"
}
}
}
The react domain is set to the "recommended", and the test domain is set to the "all". The react domain enables the stable React rules, and the test domain enables every test rule, including the nursery ones . The two domains are independent, and the configuration combines them. The rules.recommended flag controls the general rules, and the domains object controls the technology-specific rules.
The auto-detection is the default behavior when the domains object is absent. Biome reads the package.json and enables the domains whose dependencies are declared. The react domain is enabled when react is a dependency, the test domain when jest or vitest is a dependency, and the next domain when next is a dependency . The detection is the mechanism that makes the zero-config setup project-aware.
// biome.json — the explicit override of the auto-detection
{
"linter": {
"domains": {
"react": "none",
"solid": "recommended"
}
}
}
The explicit domains object overrides the detection. The react: "none" disables the React rules even if react is in the package.json, and the solid: "recommended" enables the Solid rules . The override is the mechanism that lets a project correct the detection when the dependency is a transitive dependency or the project uses a different framework than the detection suggests.
The domain and the group can both configure the same rule. The domain enables the useExhaustiveDependencies, and the explicit linter.rules.correctness.useExhaustiveDependencies disables it. The explicit rule configuration wins, and the conflict is the mechanism that lets the project keep the domain while disabling the specific rule.
// biome.json — the explicit rule disables the domain rule
{
"linter": {
"rules": {
"correctness": {
"useExhaustiveDependencies": "off"
}
},
"domains": { "react": "recommended" }
}
}
The react domain enables the rule, and the explicit correctness configuration disables it. The rule is off, and the other React rules stay on. The conflict resolution is the mechanism that gives the project the granularity it needs.
b. The domain catalog and the globals
The domains cover the frameworks, the environments, and the project-level analysis. The framework domains are the react, the next, the solid, the vue, the svelte, the qwik, the remix, the astro, the react-native, the drizzle, and the tailwind. The environment domains are the test, the playwright, the jest, the vitest, the bun, the tailwind, and the turborepo. The analysis domains are the project and the types .
// biome.json — the framework and the environment domains
{
"linter": {
"domains": {
"react": "recommended",
"next": "recommended",
"test": "recommended",
"playwright": "recommended"
}
}
}
The react and the next domains enable the framework rules, and the test and the playwright domains enable the environment rules. The four domains are independent, and the configuration is the explicit set for a Next.js project that uses the Vitest and the Playwright . The next domain requires the next dependency of version 14.0.0 or later, and the playwright domain requires the @playwright/test dependency .
The test domain is the one that adds the globals. When the domain is enabled, Biome recognises the describe, the it, the expect, the beforeEach, the afterEach, the beforeAll, the afterAll, the test, the suite, and the other test APIs as the known globals . The globals are the mechanism that prevents the noUndeclaredVariables diagnostic from firing on the test files. The playwright domain adds the test and the expect globals in the same way .
// biome.json — the test domain with the globals
{
"linter": {
"domains": { "test": "recommended" }
}
}
The test domain enables the rules and the globals. The noFocusedTests and the noExportsInTest are the recommended rules, and the describe and the it are the globals . The domain is the single configuration that covers both the rules and the environment.
The project domain is the one that performs the cross-file analysis. The noPrivateImports, the noUndeclaredDependencies, the noUnresolvedImports, and the useImportExtensions are the rules, and the noPrivateImports is the recommended one . The domain scans the entire project, and the scan is the performance cost that the domain makes opt-in.
// biome.json — the project domain
{
"linter": {
"domains": { "project": "recommended" }
}
}
The project domain enables the cross-file rules. The noPrivateImports is the recommended rule, and the scan is the mechanism that resolves the module graph . The domain is the opt-in for the project that wants the import analysis, and the performance cost is the trade-off.
Complete Example Session
// ============================================
// PART 1: THE AUTO-DETECTION
// ============================================
// The package.json has react and vitest.
// The react and the test domains are enabled automatically.
// No explicit domains configuration is needed.
// ============================================
// PART 2: THE EXPLICIT REACT DOMAIN
// ============================================
{
"linter": {
"rules": { "recommended": true },
"domains": { "react": "recommended" }
}
}
// The React-specific stable rules are enabled.
// The useExhaustiveDependencies and the noArrayIndexKey are on.
// ============================================
// PART 3: THE TEST DOMAIN WITH THE GLOBALS
// ============================================
{
"linter": {
"domains": { "test": "recommended" }
}
}
// The test rules and the test globals are enabled.
// The describe, the it, and the expect are recognised.
// ============================================
// PART 4: THE TEST DOMAIN WITH ALL THE RULES
// ============================================
{
"linter": {
"domains": { "test": "all" }
}
}
// Every test rule is enabled, including the nursery ones.
// The useConsistentTestIt and the useTestHooksInOrder are on.
// ============================================
// PART 5: THE DOMAIN OVERRIDE
// ============================================
{
"linter": {
"domains": {
"react": "none",
"solid": "recommended"
}
}
}
// The react domain is disabled.
// The solid domain is enabled.
// The override corrects the auto-detection.
// ============================================
// PART 6: THE DOMAIN AND THE GROUP CONFLICT
// ============================================
{
"linter": {
"rules": {
"correctness": { "useExhaustiveDependencies": "off" }
},
"domains": { "react": "recommended" }
}
}
// The react domain enables the rule.
// The explicit group config disables it.
// The explicit config wins.
// ============================================
// PART 7: THE TEST DOMAIN SCOPED TO THE FILES
// ============================================
{
"linter": {
"domains": { "react": "recommended" }
},
"overrides": [
{
"includes": ["**/*.test.ts"],
"linter": { "domains": { "test": "all" } }
}
]
}
// The react domain is on for every file.
// The test domain is on only for the test files.
// ============================================
// PART 8: THE PLAYWRIGHT DOMAIN
// ============================================
{
"linter": {
"domains": { "playwright": "recommended" }
}
}
// The Playwright rules are enabled.
// The test and the expect globals are recognised.
// ============================================
// PART 9: THE PROJECT DOMAIN
// ============================================
{
"linter": {
"domains": { "project": "recommended" }
}
}
// The cross-file analysis is enabled.
// The noPrivateImports rule is on.
// The scan has the performance cost.
// ============================================
// PART 10: THE CHECKLIST
// ============================================
// [ ] The domains are auto-detected from the package.json.
// [ ] The explicit domains override the detection.
// [ ] The mode is "all", "recommended", or "none".
// [ ] The test domain adds the globals.
// [ ] The explicit group config wins the conflict.
// [ ] The project domain is opt-in for the performance.
The ten parts covered the auto-detection, the explicit React domain, the test domain with the globals, the test domain with all the rules, the domain override, the domain and the group conflict, the test domain scoped to the files, the Playwright domain, the project domain, and the checklist. The through-line is that the domain is the technology axis, and the auto-detection is the mechanism that makes the default relevant.
Quick Reference
The domain modes
| Mode | Behavior |
|---|---|
"all" | Every rule in the domain, including the nursery |
"recommended" | The stable recommended rules of the domain |
"none" | The domain is explicitly disabled |
The domain catalog
| Domain | Detected from | Purpose |
|---|---|---|
react | react >= 16.0.0 | The React rules |
next | next >= 14.0.0 | The Next.js rules |
solid | solid-js | The Solid rules |
vue | vue | The Vue rules |
test | jest >= 26, mocha >= 8, vitest | The test rules and globals |
playwright | @playwright/test >= 1.0.0 | The Playwright rules |
project | Manual opt-in | The cross-file analysis |
types | Manual opt-in | The type inference rules |
The test globals
| Global | Purpose |
|---|---|
describe | The test suite |
it / test | The test case |
expect | The assertion |
beforeEach / afterEach | The setup and teardown |
beforeAll / afterAll | The suite-level setup |
The conflict resolution
| Configuration | Result |
|---|---|
| The domain enables the rule | The rule is on |
| The explicit group disables the rule | The rule is off |
| The explicit group enables the rule | The rule is on |
Best Practices
✅ Do This:
// Let the auto-detection enable the domains
// No explicit configuration needed for a standard project. // ✅
// Override the detection when the dependency is transitive
{ "domains": { "react": "none" } } // ✅
// Scope the test domain to the test files
{ "overrides": [{ "includes": ["**/*.test.ts"], "linter": { "domains": { "test": "all" } } }] } // ✅
❌ Don’t Do This:
// Assume the domains are always on
// The auto-detection depends on the package.json. // ❌
// Enable the project domain without the performance budget
{ "domains": { "project": "all" } } // the full scan // ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| The React rule is not applied | The react domain is not detected | Add the explicit domains.react |
| The test globals are flagged | The test domain is not enabled | Set the domains.test |
| The domain rule is still on | The explicit group config does not disable it | Check the rule name |
| The linting is slow | The project domain is enabled | Disable it or scope it |
Real-World Examples
1. The auto-detection
// The package.json has react.
// The react domain is enabled automatically.
2. The explicit React domain
{ "domains": { "react": "recommended" } }
3. The test domain
{ "domains": { "test": "all" } }
4. The domain override
{ "domains": { "react": "none", "solid": "recommended" } }
5. The test domain scoped to the files
{ "overrides": [{ "includes": ["**/*.test.ts"], "linter": { "domains": { "test": "all" } } }] }
6. The Playwright domain
{ "domains": { "playwright": "recommended" } }
7. The project domain
{ "domains": { "project": "recommended" } }
8. The conflict resolution
{ "rules": { "correctness": { "useExhaustiveDependencies": "off" } }, "domains": { "react": "recommended" } }
9. The Vue domain
{ "domains": { "vue": "recommended" } }
10. The types domain
{ "domains": { "types": "recommended" } }
Visual
The Auto-Detection
┌──────────────────────────────────────────────────────────────┐
│ THE AUTO-DETECTION READS THE PACKAGE.JSON │
│ │
│ package.json │
│ ┌─────────────────────┐ │
│ │ "react": "^18" │ The react dependency │
│ │ "vitest": "^1" │ The test dependency │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ The react domain │ Enabled automatically │
│ │ The test domain │ Enabled automatically │
│ └─────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
The Domain and the Group
┌──────────────────────────────────────────────────────────────┐
│ THE TWO AXES │
│ │
│ The group: correctness │
│ ┌─────────────────────┐ │
│ │ useExhaustive │ The problem category │
│ │ Dependencies │ │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ The domain: react │
│ ┌─────────────────────┐ │
│ │ useExhaustive │ The technology │
│ │ Dependencies │ │
│ └─────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
The Conflict Resolution
┌──────────────────────────────────────────────────────────────┐
│ THE EXPLICIT RULE WINS │
│ │
│ domains.react: "recommended" │
│ ┌─────────────────────┐ │
│ │ useExhaustive │ Enabled by the domain │
│ │ Dependencies: error │ │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ rules.correctness │
│ ┌─────────────────────┐ │
│ │ useExhaustive │ Disabled by the explicit rule │
│ │ Dependencies: off │ │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ The rule is off │ The explicit config wins │
│ └─────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| The domain | The technology-specific rule set |
| The auto-detection | From the package.json dependencies |
| The modes | "all", "recommended", "none" |
| The test domain | The rules and the globals |
| The project domain | The cross-file analysis with the performance cost |
| The conflict | The explicit rule config wins |
Key takeaways:
- Domains group rules by technology, orthogonally to the problem groups. The
react, thenext, thetest, and the others are the named collections, and a rule belongs to both a group and a domain . The domain is the framework axis, and the group is the problem axis. - The auto-detection reads the
package.json. Biome enables the domains whose dependencies are declared, so a project withreactandvitestgets thereactand thetestdomains without explicit configuration . The explicitdomainsobject overrides the detection. - The
"recommended"mode enables the stable rules and skips the nursery. The"all"mode includes the nursery rules, and the"none"disables the domain . The distinction is the mechanism that lets the domain evolve without breaking the projects. - The
testdomain adds the globals. Thedescribe, theit, theexpect, and the other test APIs are recognised as the known globals, which prevents thenoUndeclaredVariablesdiagnostic on the test files . Theplaywrightdomain adds thetestand theexpectglobals. - The
projectdomain has a performance cost. The cross-file analysis scans the entire project, and the domain is opt-in for the projects that need the import analysis . The cost is the trade-off for the module graph resolution.
Remember: The Biome domains are the technology axis on top of the problem groups. The auto-detection makes the zero-config setup relevant, and the explicit configuration is the override. The "recommended" is the stable set, the "all" is the complete set, and the "none" is the disable. The test domain adds the globals, and the project domain adds the cross-file analysis with the performance cost. The explicit rule config wins the conflict.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!