| | |

Biome 07 🧹 Linting Rules — Rule groups and severity levels

⏱️ Estimated read: 13 min | |

Biome’s linter is organized around rules, and every rule belongs to a group. The group is the taxonomy—the category of the problem the rule detects—and the rule is the specific check. The noDebugger rule is in the suspicious group, because a stray debugger statement is likely an accident. The noUnusedVariables rule is in the correctness group, because an unused variable is a definite mistake . The grouping is the mechanism that lets a project enable or disable an entire category without enumerating every rule, and it is the first layer of the configuration.

The severity is the second layer. Every rule emits a diagnostic with one of three active levels—info, warn, or error—or it is turned off. The error severity causes the CLI to exit with a non-zero code, which is the mechanism that fails the CI pipeline. The warn and the info produce the diagnostics without failing the command, and the --error-on-warnings flag is the mechanism that promotes the warnings to the errors for a single invocation . The severity is the gate, and the group is the category.

Key point: The rules are grouped into correctness, suspicious, style, complexity, performance, security, a11y, and nursery. The severity levels are off, info, warn, and error. The error fails the CLI, the warn fails only with --error-on-warnings, and the info never fails the CLI. The recommended flag enables the curated set, and the "on" value enables a rule with its own default severity.


Why the groups and the severities are separate

The group problem. A backend project has no use for the a11y rules, because accessibility is a browser concern. The "a11y": "off" is the mechanism that disables the whole group without listing the individual rules . The group is the coarse control, and the rule is the fine control. A project that is migrating from ESLint can map the ESLint plugin categories to the Biome groups, and the mapping is the mechanism that makes the migration tractable.

The severity problem. A project that adopts Biome on an existing codebase has hundreds of violations, and the CI would fail on all of them. The warn severity is the mechanism that reports the violations without failing the CI, and the --error-on-warnings flag is the mechanism that promotes the warnings to the errors when the project is ready to enforce them . The severity is the ratchet, and the ratchet is the migration mechanism. The info severity is the third level, and it is the mechanism for the diagnostics that are informational and should never fail the pipeline.

The default severity problem. A rule has its own default severity, and the "on" value enables the rule with that default. The noShoutyConstants rule is not recommended by default, and when it is triggered it emits the info severity. The "on" is the mechanism that keeps the default, and the "error" is the mechanism that overrides it . The distinction matters because a project that sets every rule to "on" and expects the CI to fail will be surprised by the rules that default to info.

The recommended problem. The recommended flag enables the curated set of rules, and the recommended rules emit the error severity by default . The flag is the mechanism that turns on the curated baseline, and the "recommended": false is the mechanism that turns it off for a project that wants only the few rules it explicitly lists. The recommended set is the starting point, and the explicit rules are the refinement.

The override problem. The rule configuration is per-rule, and the file-specific configuration is the override. The javascript.linter.rules is not a valid key, and the overrides array with the file globs is the mechanism for the language-specific rules . The override merges with the top-level config, and the merge is the mechanism that keeps the configuration maintainable. A project that wants the test files to have the different rules uses the overrides array rather than duplicating the whole config.


a. The rule groups and the configuration shape

The linter.rules object contains the groups, and each group contains the rules. The group can be configured as a whole with a severity string, or as an object with the individual rules.

// biome.json — the group-level configuration
{
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "suspicious": {
        "noDebugger": "error",
        "noExplicitAny": "warn"
      },
      "a11y": "off"
    }
  }
}

The suspicious group is configured as an object with the two rules. The a11y group is configured as the "off" string, which disables the whole group . The two forms are the same mechanism, and the group-level string is the shorthand for the whole group. The group-level string is the mechanism that lets a backend project disable the a11y group in one line.

The recommended flag is the top-level switch. The true is the default, and the recommended rules are enabled. The false is the mechanism that disables the whole recommended set, which is useful when the project wants only the few rules it explicitly lists.

// biome.json — the rule with the options
{
  "linter": {
    "rules": {
      "style": {
        "useNamingConvention": {
          "level": "error",
          "options": { "strictCase": false }
        }
      }
    }
  }
}

The rule with the options is the object form. The level is the severity, and the options is the rule-specific configuration . The useNamingConvention is the example, and the strictCase is the option that the rule accepts. The object form is the mechanism that configures the rule beyond the severity.

The fix option is the third field. The "none" disables the code fix, the "safe" keeps the safe fix, and the "unsafe" keeps the unsafe fix . The option is the mechanism that controls the autofix behavior for the specific rule.

// biome.json — the rule with the fix option
{
  "linter": {
    "rules": {
      "correctness": {
        "noUnusedVariables": {
          "level": "error",
          "fix": "none"
        }
      }
    }
  }
}

The noUnusedVariables with the fix: "none" is the rule that reports the unused variables but does not apply the automatic removal. The fix is the mechanism that removes the variable, and the none is the mechanism that keeps the manual review.


b. The severity ratchet and the CLI flags

The severity ratchet is the migration pattern. The recommended rules with no violations keep the error severity. The rules with the existing violations are pinned to the warn, so the CI stays green while the violations are burned down. The --error-on-warnings is the flag that promotes the warnings to the errors for the CI run that wants to enforce the ratchet .

// biome.json — the ratchet configuration
{
  "linter": {
    "rules": {
      "recommended": true,
      "correctness": {
        "useExhaustiveDependencies": "warn",
        "noUnusedImports": "warn"
      }
    }
  }
}

The useExhaustiveDependencies and the noUnusedImports are pinned to the warn. The biome ci does not fail on the warnings, and the CI stays green while the violations are fixed . The --error-on-warnings is the mechanism that turns the warnings into the errors once the violations reach zero.

The --only and the --skip flags are the CLI overrides. The --only runs the specific rule or the group, and the --skip disables the specific rule or the group for the single invocation .

# The --only flag runs the specific rule
biome lint --only=style/useNamingConvention ./src

# The --skip flag disables the group
biome lint --skip=style ./src

The --only and the --skip are the mechanism that overrides the configuration for the single command. The --only is the mechanism for the targeted check, and the --skip is the mechanism for the temporary exclusion. A developer who is debugging a specific rule uses the --only to run just that rule, and a developer who is refactoring a specific area uses the --skip to disable the group for the duration.

The overrides array is the file-specific configuration. The javascript.linter.rules is not a valid key, and the overrides with the file globs is the mechanism for the language-specific rules .

// biome.json — the overrides array
{
  "linter": {
    "rules": {
      "recommended": true,
      "nursery": "on"
    }
  },
  "overrides": [
    {
      "includes": ["**/*.ts", "**/*.tsx"],
      "linter": {
        "rules": {
          "nursery": {
            "useConsistentTypeDefinitions": {
              "level": "on",
              "options": { "style": "type" }
            }
          }
        }
      }
    }
  ]
}

The top-level nursery: "on" enables all the nursery rules. The override enables the useConsistentTypeDefinitions with the options for the TypeScript files. The override merges with the top-level config, and the merge is the mechanism that keeps the configuration maintainable .


Complete Example Session

// ============================================
// PART 1: THE GROUP-LEVEL CONFIGURATION
// ============================================

{
  "linter": {
    "rules": {
      "recommended": true,
      "suspicious": {
        "noDebugger": "error"
      },
      "a11y": "off"
    }
  }
}

// The suspicious group has the noDebugger at the error.
// The a11y group is disabled.

// ============================================
// PART 2: THE RULE WITH THE OPTIONS
// ============================================

{
  "linter": {
    "rules": {
      "style": {
        "useNamingConvention": {
          "level": "error",
          "options": { "strictCase": false }
        }
      }
    }
  }
}

// The rule has the level and the options.

// ============================================
// PART 3: THE FIX OPTION
// ============================================

{
  "linter": {
    "rules": {
      "correctness": {
        "noUnusedVariables": {
          "level": "error",
          "fix": "none"
        }
      }
    }
  }
}

// The rule reports but does not fix.

// ============================================
// PART 4: THE RATCHET
// ============================================

{
  "linter": {
    "rules": {
      "recommended": true,
      "correctness": {
        "useExhaustiveDependencies": "warn"
      }
    }
  }
}

// The rule is pinned to the warn during the migration.

// ============================================
// PART 5: THE --ERROR-ON-WARNINGS
// ============================================

// biome ci --error-on-warnings ./src

// The flag promotes the warnings to the errors.
// The CI fails if any warning is present.

// ============================================
// PART 6: THE --ONLY FLAG
// ============================================

// biome lint --only=style/useNamingConvention ./src

// The command runs only the specified rule.

// ============================================
// PART 7: THE --SKIP FLAG
// ============================================

// biome lint --skip=style ./src

// The command skips the whole style group.

// ============================================
// PART 8: THE OVERRIDES ARRAY
// ============================================

{
  "linter": {
    "rules": { "recommended": true }
  },
  "overrides": [
    {
      "includes": ["**/*.test.ts"],
      "linter": {
        "rules": {
          "suspicious": { "noExplicitAny": "off" }
        }
      }
    }
  ]
}

// The test files have the noExplicitAny disabled.
// The override merges with the top-level config.

// ============================================
// PART 9: THE DOMAIN
// ============================================

{
  "linter": {
    "domains": {
      "react": "recommended"
    }
  }
}

// The react domain is enabled.
// The domain is the grouping by the technology.

// ============================================
// PART 10: THE CHECKLIST
// ============================================

// [ ] The recommended rules are enabled.
// [ ] The groups are configured with the severity strings.
// [ ] The rules with the options use the object form.
// [ ] The fix option is set for the rules that need it.
// [ ] The ratchet uses the warn severity during the migration.
// [ ] The --error-on-warnings is used in the CI.
// [ ] The overrides array is used for the file-specific rules.

The ten parts covered the group-level configuration, the rule with the options, the fix option, the ratchet, the --error-on-warnings, the --only flag, the --skip flag, the overrides array, the domain, and the checklist. The through-line is that the group is the coarse control, the rule is the fine control, and the severity is the gate.


Quick Reference

The rule groups

GroupPurpose
correctnessThe code that is definitely wrong
suspiciousThe code that is likely wrong
styleThe consistent code style
complexityThe code that is too complex
performanceThe code that is slow
securityThe code that is vulnerable
a11yThe accessibility rules
nurseryThe new rules that are not yet stable

The severity levels

SeverityCLI exitPurpose
offNoThe rule is disabled
infoNoThe informational diagnostic, never fails
warnNo (unless --error-on-warnings)The warning, CI stays green
errorYesThe error, CI fails

The rule configuration shapes

ShapeExamplePurpose
The string"noDebugger": "error"The severity only
The object"useNamingConvention": { "level": "error", "options": {} }The severity and the options
The group string"a11y": "off"The whole group disabled

The CLI flags

FlagPurpose
--error-on-warningsPromotes the warnings to the errors
--only=<rule>Runs only the specified rule or group
--skip=<rule>Skips the specified rule or group
--writeApplies the safe fixes
--write --unsafeApplies the safe and unsafe fixes

Best Practices

✅ Do This:

// Use the group-level string for the whole group
{ "a11y": "off" }                                                 // ✅

// Use the object form for the rule with the options
{ "useNamingConvention": { "level": "error", "options": {} } }    // ✅

// Use the warn severity for the ratchet
{ "useExhaustiveDependencies": "warn" }                           // ✅

❌ Don’t Do This:

// Use the javascript.linter.rules key
{ "javascript": { "linter": { "rules": {} } } } // the invalid key  // ❌

// Assume the "on" is the error severity
{ "noShoutyConstants": "on" } // the default is the info           // ❌

Common Pitfalls

PitfallWhy It HappensFix
The group is disabled but the rule is notThe rule is in the overridesCheck the overrides array
The "on" does not fail the CIThe default severity is infoUse the "error" explicitly
The language-specific rule is ignoredThe javascript.linter.rules is invalidUse the overrides array
The ratchet never completesThe warn is never promotedUse the --error-on-warnings

Real-World Examples

1. The group-level configuration

{ "linter": { "rules": { "a11y": "off" } } }

2. The rule with the severity

{ "linter": { "rules": { "suspicious": { "noDebugger": "error" } } } }

3. The rule with the options

{ "linter": { "rules": { "style": { "useNamingConvention": { "level": "error", "options": {} } } } } }

4. The fix option

{ "linter": { "rules": { "correctness": { "noUnusedVariables": { "level": "error", "fix": "none" } } } } }

5. The ratchet

{ "linter": { "rules": { "correctness": { "useExhaustiveDependencies": "warn" } } } }

6. The –error-on-warnings

biome ci --error-on-warnings ./src

7. The –only flag

biome lint --only=style/useNamingConvention ./src

8. The –skip flag

biome lint --skip=style ./src

9. The overrides array

{ "overrides": [{ "includes": ["**/*.test.ts"], "linter": { "rules": { "suspicious": { "noExplicitAny": "off" } } } }] }

10. The domain

{ "linter": { "domains": { "react": "recommended" } } }

Visual

The Group and the Rule

┌──────────────────────────────────────────────────────────────┐
│  THE GROUP IS THE COARSE CONTROL                             │
│                                                              │
│  suspicious                                                  │
│  ┌─────────────────────┐                                    │
│  │ noDebugger: error   │  The rule                          │
│  │ noExplicitAny: warn │  The rule                          │
│  └─────────────────────┘                                    │
│                                                              │
│  a11y: "off"                                                 │
│  ┌─────────────────────┐                                    │
│  │ The whole group is  │  The group-level string            │
│  │ disabled            │                                    │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

The Severity Ladder

┌──────────────────────────────────────────────────────────────┐
│  THE SEVERITY LEVELS                                         │
│                                                              │
│  error                                                       │
│  ┌─────────────────────┐                                    │
│  │ The CLI exits 1     │  The CI fails                      │
│  └─────────────────────┘                                    │
│                                                              │
│  warn                                                        │
│  ┌─────────────────────┐                                    │
│  │ The CLI exits 0     │  The CI passes                     │
│  │ unless --error-on-  │                                    │
│  │ warnings            │                                    │
│  └─────────────────────┘                                    │
│                                                              │
│  info                                                        │
│  ┌─────────────────────┐                                    │
│  │ The CLI exits 0     │  Even with --error-on-warnings     │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

The Override

┌──────────────────────────────────────────────────────────────┐
│  THE OVERRIDE MERGES WITH THE TOP-LEVEL                      │
│                                                              │
│  linter.rules                                                │
│  ┌─────────────────────┐                                    │
│  │ recommended: true   │  The baseline                      │
│  │ nursery: "on"       │  All the nursery rules             │
│  └──────────┬──────────┘                                    │
│             │                                                │
│             ▼                                                    │
│  overrides                                                   │
│  ┌─────────────────────┐                                    │
│  │ includes: *.ts      │  The TypeScript files              │
│  │ useConsistentType-  │  The specific rule with the        │
│  │ Definitions         │  options                           │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
The rule groupscorrectness, suspicious, style, complexity, performance, security, a11y, nursery
The severity levelsoff, info, warn, error
The recommendedThe recommended: true enables the curated set
The "on"Enables the rule with its default severity
The ratchetThe warn during the migration, the error after
The --error-on-warningsPromotes the warnings to the errors
The overridesThe file-specific rule configuration

Key takeaways:

  • The rules are grouped by the kind of problem they detect. The correctness group is the definitely-wrong code, the suspicious group is the likely-wrong code, and the a11y group is the accessibility. The group is the coarse control, and the rule is the fine control .
  • The severity is the gate. The error fails the CLI, the warn fails only with --error-on-warnings, and the info never fails the CLI . The --error-on-warnings is the flag that promotes the warnings to the errors for the CI run.
  • The "on" keeps the rule’s default severity. A rule that defaults to the info does not fail the CI when it is set to the "on". The "error" is the explicit override .
  • The overrides array is the file-specific configuration. The javascript.linter.rules is not a valid key, and the overrides with the file globs is the mechanism for the language-specific rules .
  • The ratchet is the migration pattern. The rules with the violations are pinned to the warn, and the CI stays green while the violations are fixed. The --error-on-warnings is the mechanism that promotes the warnings once the violations reach zero .

Remember: The Biome linter is the rules, the groups, and the severities. The group is the coarse control, the rule is the fine control, and the severity is the gate. The recommended enables the curated set, the "on" keeps the default severity, and the "error" fails the CI. The overrides array is the file-specific configuration, and the ratchet is the migration pattern.

Was this helpful?


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!