Skip to content

Fix: Biome Not Working — Rules Not Applied, ESLint Config Not Migrated, or VSCode Extension Ignored

FixDevs · (Updated: )

Part of:  React & Frontend Errors

Quick Answer

How to fix Biome linter/formatter issues — biome.json configuration, migrating from ESLint and Prettier, VSCode extension setup, CI integration, and rule override syntax.

The Problem

Biome runs but reports no errors on code that should fail:

biome check src/
# Checked 42 file(s) in 23ms
# Found 0 errors.  ← But obvious issues exist

Or ESLint rules migrated to Biome don’t match:

biome migrate eslint --write
# Some rules could not be migrated: no-console, import/order

Or the VSCode extension isn’t formatting on save despite being installed:

// .vscode/settings.json
{
  "editor.formatOnSave": true
}
// Files still format with Prettier instead of Biome

Or Biome and TypeScript disagree on valid code:

// Biome reports: noExplicitAny — but this is intentional
function serialize(value: any): string { ... }

Why This Happens

Biome is a newer tool with different conventions from ESLint + Prettier. It is the spiritual successor to Rome (which was archived in 2023) and is now maintained by an independent team. The architecture is a single Rust binary with no plugin system, no JS-based config evaluation, and no peer-dependency graph — which is why it is fast, and also why some ESLint rules cannot be ported.

  • Rules are opt-in per group"recommended": true enables recommended rules, but many useful rules require explicit enablement. Without configuring rule groups, most checks are disabled.
  • Biome has fewer rules than ESLint — some ESLint plugins (especially ecosystem-specific ones like eslint-plugin-react-hooks, eslint-plugin-import) don’t have Biome equivalents yet. The migration tool marks these as unsupported.
  • VSCode uses editor.defaultFormatter — to use Biome as the formatter in VSCode, you must set "editor.defaultFormatter": "biomejs.biome" explicitly, not just install the extension.
  • Biome linter and formatter are independent — disabling one doesn’t affect the other. If you only want the formatter (replacing Prettier), configure "linter": { "enabled": false }.

A frequent surprise is that Biome does not read tsconfig.json for type information. ESLint with @typescript-eslint/parser and parserOptions.project can run rules like no-floating-promises, no-misused-promises, and no-unnecessary-condition because it has the full TypeScript type graph. Biome only does syntactic and lexical analysis — fast, but blind to types. Rules that need type information will not appear in Biome at all, no matter what version you upgrade to, unless the team adds a separate type-aware mode (announced for late 2026 but not shipped as of this writing).

Configuration-file precedence also bites. Biome walks up from each file looking for biome.json or biome.jsonc. If you have one config in the repo root and another in a subdirectory, files under that subdirectory use the local config and inherit nothing from the root unless "extends" is explicit. Many teams ship a root config and then wonder why one package skips all rules — it has a stray biome.json that overrides defaults to "recommended": false.

Fix 1: Set Up biome.json Correctly

Biome is configured through biome.json at the project root:

# Initialize Biome in your project
npm install --save-dev --save-exact @biomejs/biome
npx biome init  # Creates biome.json with defaults
// biome.json — complete recommended setup
{
  "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true  // Respect .gitignore for file selection
  },
  "files": {
    "ignoreUnknown": false,
    "ignore": [
      "dist",
      "build",
      ".next",
      "node_modules",
      "coverage",
      "*.min.js",
      "**/__generated__/**"
    ]
  },
  "formatter": {
    "enabled": true,
    "formatWithErrors": false,
    "indentStyle": "space",    // "tab" or "space"
    "indentWidth": 2,
    "lineEnding": "lf",        // "lf", "crlf", "cr"
    "lineWidth": 100,          // Print width (like Prettier's)
    "attributePosition": "auto"  // HTML/JSX attribute wrapping
  },
  "organizeImports": {
    "enabled": true  // Auto-sort imports
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,    // Enable all recommended rules
      "correctness": {
        "recommended": true,
        "noUnusedVariables": "error",
        "noUnusedImports": "error"
      },
      "suspicious": {
        "recommended": true,
        "noConsole": "warn"   // Warn on console.log
      },
      "style": {
        "recommended": true,
        "useConst": "error",
        "noVar": "error"
      },
      "performance": {
        "recommended": true
      },
      "security": {
        "recommended": true
      },
      "a11y": {
        "recommended": true   // Accessibility rules
      },
      "complexity": {
        "recommended": true
      }
    }
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single",          // "single" or "double"
      "jsxQuoteStyle": "double",        // JSX attribute quotes
      "quoteProperties": "asNeeded",    // Only quote object keys when needed
      "trailingCommas": "all",          // "all", "es5", "none"
      "semicolons": "always",           // "always" or "asNeeded"
      "arrowParentheses": "always",     // "(x) => x" vs "x => x"
      "bracketSpacing": true,           // { key: value }
      "bracketSameLine": false          // JSX closing bracket position
    },
    "parser": {
      "unsafeParameterDecoratorsEnabled": true  // For Angular/NestJS decorators
    }
  }
}

Fix 2: Migrate from ESLint and Prettier

# Migrate ESLint config to Biome (reads .eslintrc.* and eslint.config.*)
npx biome migrate eslint --write

# Migrate Prettier config to Biome (reads .prettierrc)
npx biome migrate prettier --write

# After migration, review the generated biome.json
# Check for "inspired" comments — these are approximate equivalents

Manually map common ESLint rules to Biome:

ESLint RuleBiome Equivalent
no-unused-varscorrectness/noUnusedVariables
no-consolesuspicious/noConsole
prefer-conststyle/useConst
no-varstyle/noVar
eqeqeqsuspicious/noDoubleEquals
no-shadowcorrectness/noShadow
react-hooks/rules-of-hookscorrectness/useHookAtTopLevel
react-hooks/exhaustive-depscorrectness/useExhaustiveDependencies
jsx-a11y/alt-texta11y/useAltText

Rules not yet in Biome — keep ESLint for these:

# If you need import/order, no-restricted-imports, or plugin-specific rules,
# run both tools: Biome for formatting + basic linting, ESLint for complex rules
# This is a common transition strategy
// package.json — run both during transition
{
  "scripts": {
    "lint": "biome check . && eslint src --ext .ts,.tsx",
    "format": "biome format --write ."
  }
}

Fix 3: Configure VSCode Extension

// .vscode/settings.json — enable Biome as the default formatter
{
  // Set Biome as default for JS/TS files
  "[javascript]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[javascriptreact]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[typescript]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[json]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[jsonc]": {
    "editor.defaultFormatter": "biomejs.biome"
  },

  // Format and fix on save
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "quickfix.biome": "explicit",    // Auto-fix safe issues on save
    "source.organizeImports.biome": "explicit"  // Sort imports on save
  },

  // Disable Prettier and ESLint for files Biome handles
  "prettier.enable": false,
  "eslint.enable": false  // Or keep enabled if running both
}

Verify the extension is using the right Biome binary:

// .vscode/settings.json
{
  // Point to local Biome binary (avoids version mismatches)
  "biome.lspBin": "./node_modules/@biomejs/biome/bin/biome"
}
# Install Biome extension from VSCode marketplace
# Extension ID: biomejs.biome

# Test that Biome works from the CLI
npx biome check --apply src/

Fix 4: Override Specific Rules per File or Directory

Disable rules for specific files or add exceptions:

// biome.json
{
  "linter": {
    "rules": {
      "suspicious": {
        "noExplicitAny": "error"  // Default: error
      }
    }
  },
  "overrides": [
    {
      // Disable rules for test files
      "include": ["**/*.test.ts", "**/*.spec.ts", "**/__tests__/**"],
      "linter": {
        "rules": {
          "suspicious": {
            "noExplicitAny": "off"  // Allow 'any' in tests
          },
          "correctness": {
            "noUnusedVariables": "off"  // Allow unused variables in tests
          }
        }
      }
    },
    {
      // Different formatting for Markdown
      "include": ["**/*.md"],
      "formatter": {
        "lineWidth": 80
      }
    },
    {
      // Generated files — only format, don't lint
      "include": ["src/generated/**"],
      "linter": {
        "enabled": false
      }
    }
  ]
}

Inline suppression (like eslint-disable):

// Suppress a single rule for one line
// biome-ignore lint/suspicious/noExplicitAny: third-party type doesn't provide types
function serialize(value: any): string {
  return JSON.stringify(value);
}

// Suppress the formatter for a block
// biome-ignore format: manually formatted for readability
const matrix = [
  1, 0, 0,
  0, 1, 0,
  0, 0, 1,
];

Fix 5: CI Integration

# GitHub Actions
jobs:
  biome:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm

      - run: npm ci

      # Check formatting and lint — fail on any issue
      - run: npx biome ci .
      # Equivalent to: biome check --error-on-warnings .

      # Or use the official GitHub Action
      - uses: biomejs/setup-biome@v2
        with:
          version: latest
      - run: biome ci .
// package.json — useful scripts
{
  "scripts": {
    "check": "biome check .",          // Check without writing
    "check:apply": "biome check --apply .",  // Fix safe issues
    "check:apply-all": "biome check --apply-unsafe .",  // Fix all (may change semantics)
    "format": "biome format --write .",
    "lint": "biome lint .",
    "ci": "biome ci ."                 // CI mode — no writes, exits with error code
  }
}

Pre-commit hook with lint-staged:

// package.json
{
  "lint-staged": {
    "*.{js,jsx,ts,tsx,json}": [
      "biome check --apply --no-errors-on-unmatched"
    ]
  }
}
# Install and configure lint-staged + husky
npm install --save-dev lint-staged husky
npx husky init
echo "npx lint-staged" > .husky/pre-commit

Fix 6: Replace Prettier Completely

If replacing Prettier with Biome as the sole formatter:

# Remove Prettier and related packages
npm uninstall prettier @prettier/plugin-xml prettier-plugin-tailwindcss \
  eslint-config-prettier eslint-plugin-prettier

# Remove Prettier config files
rm .prettierrc .prettierrc.js .prettierignore

# Update .editorconfig to match Biome settings (optional)
// biome.json — Prettier-compatible settings
{
  "formatter": {
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 80
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "double",       // Prettier default
      "trailingCommas": "all",      // Prettier default (ES5+)
      "semicolons": "always",
      "arrowParentheses": "always"
    }
  }
}

Notable Biome formatting differences from Prettier:

  • Biome adds trailing commas in function parameters (Prettier doesn’t in some cases)
  • Biome handles long import statements differently in some edge cases
  • Biome formats template literals slightly differently for multiline cases
  • JSX formatting is generally compatible

Run biome format --write . then git diff to review all differences from Prettier before committing.

Biome vs ESLint + Prettier vs oxlint vs deno_lint + dprint vs Rome

The lint-and-format space split into four camps after Rome was archived. Picking the right one depends on whether you prioritize speed, ecosystem depth, type-aware rules, or a single binary.

Biome is one binary, one config, one team. It does lint and format together with shared parser state, which is why running both takes about as long as running either. Biome covers around 280 rules as of v1.9. The lack of plugins is intentional: every rule is reviewed and shipped in core, which keeps quality high but means you cannot write project-specific rules. Speed: roughly 15-25x faster than ESLint + Prettier combined on a typical TypeScript project.

ESLint + Prettier is the incumbent. ESLint has thousands of rules across hundreds of plugins (eslint-plugin-react, eslint-plugin-import, eslint-plugin-jsx-a11y, @typescript-eslint, framework-specific plugins for Vue, Svelte, Astro). ESLint is the only tool with deep type-aware rules thanks to @typescript-eslint. Prettier handles formatting; ESLint handles correctness. The cost is speed and config complexity — .eslintrc migration to flat config (eslint.config.js) in ESLint 9 broke many existing setups. ESLint 9+ also requires Node 18+. See Fix: ESLint Config Not Working.

oxlint is Rust, like Biome, but linter-only and aimed squarely at being an ESLint drop-in. It reimplements common ESLint rules and reads .eslintrc directly. oxlint is 50-100x faster than ESLint on the same rule set because it parallelizes per-file. It does not format. The strategy is usually “oxlint for the fast path, ESLint for type-aware rules oxlint hasn’t ported yet.” See Fix: oxlint Not Working.

deno_lint + dprint is the Deno ecosystem’s split. deno lint runs a small set of opinionated rules with zero configuration; dprint is a pluggable formatter (supports TypeScript, Markdown, TOML, Dockerfile, JSON) with WASM plugins. Together they offer Biome-like speed with a pluggable formatter — but the lint side is smaller than Biome’s. Use this combo if you are already on Deno or want dprint’s Markdown formatter, which is among the best.

Rome is deprecated. If you have an old rome.json, run biome migrate rome --write. Do not start new projects on Rome.

rslint is JetBrains’ new Rust linter (started 2025). Too early to recommend in 2026 but worth watching — it targets full ESLint compatibility including custom rules.

Rule of thumb: Biome if you want one tool with sensible defaults and no plugin sprawl. ESLint + Prettier if you depend on type-aware rules, framework plugins, or custom rules. oxlint + ESLint hybrid if ESLint is too slow but you cannot drop type-aware rules. dprint if you specifically want a fast pluggable formatter without taking on Deno.

A note on Biome’s roadmap: type-aware rules are planned for late 2026 via a TypeScript fork that Biome consumes for type info. If that lands, the biggest reason to stay on ESLint goes away. Until then, the hybrid (Biome for format + basic lint, ESLint for type-aware rules) is a defensible setup.

Still Not Working?

Biome ignores files that should be checked — verify biome.json is at the root of the project (same level as package.json). If biome.json is inside a subdirectory, Biome uses it only for that subdirectory. Run biome explain configuration to see which config file Biome is using.

biome check passes but CI fails — ensure the same Biome version is used locally and in CI. Add --exact when installing (npm install --save-dev --save-exact @biomejs/biome) to pin the version. Different Biome versions may have different rule sets.

VSCode extension shows errors that biome check doesn’t — the VSCode extension may be using a different Biome binary (global vs local) or a different config file. Set "biome.lspBin" in .vscode/settings.json to point to the local node_modules/.bin/biome binary.

TypeScript project references not supported — Biome doesn’t read tsconfig.json for type-aware linting (unlike typed ESLint rules like @typescript-eslint/no-floating-promises). Rules requiring type information are not available in Biome. If you rely heavily on typed ESLint rules, keep ESLint for those specific rules alongside Biome for formatting.

organizeImports and ESLint’s import/order produce different output — Biome groups imports by source (external, internal, relative) using a fixed order. import/order is configurable. If you ran both on the same file, each tool will undo the other’s changes. Disable one. The usual choice is to keep Biome’s organizeImports and remove the ESLint rule, since Biome runs faster and the ordering is good enough for most teams.

biome check --apply rewrites files that git diff shows no changes for — Biome’s --apply rewrites the file even when no fix was needed if the original had inconsistent line endings (crlf vs lf). On Windows clones with core.autocrlf=true, this causes phantom diffs. Set formatter.lineEnding in biome.json to match your repo’s .gitattributes, or run git config core.autocrlf false.

Monorepo: a subpackage has its own biome.json overriding the root — Biome does not auto-extend the root. Either remove the subpackage config or add "extends": ["../../biome.json"] (1.7+) so the subpackage inherits and adds overrides only.

For related tooling issues, see Fix: ESLint Config Not Working, Fix: oxlint Not Working, Fix: TypeScript Path Alias Not Working, and Fix: pnpm Workspace Not Working.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles