| | |

Biome 15 🧹 Migrating from ESLint — biome migrate eslint

⏱️ Estimated read: 12 min | |

The biome migrate eslint command is the automated bridge between an ESLint configuration and a Biome configuration. It reads the .eslintrc or the eslint.config.js file, follows the extends chains, loads the shared and the plugin configurations, and writes the equivalent rules and settings into the biome.json . The command is the first step of the migration, and the manual review is the second. The output is not a perfect reproduction of the ESLint behavior, because Biome does not implement every rule option and deliberately diverges in some cases . The review is the mechanism that catches the differences.

The command’s most important behavior is the rule-preset replacement. It does not append the migrated rules to the existing configuration. It sets the recommended preset to false and enables only the rules that it successfully migrated . The replacement is the mechanism that keeps the configuration honest: the rules that Biome could not migrate are not silently enabled by the recommended preset, and the developer sees exactly which rules are active. The migrated rules are the explicit set, and the set is the result of the translation.

Key point: biome migrate eslint --write reads the ESLint configuration, follows the extends chains, loads the plugins, and writes the equivalent rules into the biome.json. It sets the recommended preset to false and enables only the migrated rules. By default, it migrates only the identical rules; the --include-inspired flag adds the inspired rules. The .eslintignore patterns become the files.includes exceptions, and the VCS integration must be enabled to keep the .gitignore behavior.


Why the migration is not a one-to-one translation

The identical-versus-inspired problem. Biome’s rules are either identical to their ESLint sources or only inspired by them . An identical rule produces the same diagnostics and the same fixes as the ESLint rule. An inspired rule produces the similar diagnostics but may differ in the options, the edge cases, or the fix behavior. The command migrates only the identical rules by default, because the inspired rules are the ones that can change the behavior unexpectedly . The --include-inspired flag is the mechanism that opts into the broader set, and the broader set is the trade-off against the precision.

The naming-convention problem. Biome uses the camelCaseRuleName while ESLint uses the kebab-case-rule-name . The no-double-equals becomes the noDoubleEquals, and the no-unused-vars becomes the noUnusedVariables. The command handles the naming translation, and the translation is the mechanism that maps the ESLint rule to the Biome rule. The rule-source page is the reference for the mapping, and the page is the mechanism that documents the origin.

The option problem. Some Biome rules do not implement every ESLint option. The command migrates the rule with the options it supports, and the options that it does not support are dropped . The dropped options are the silent difference, and the difference is the reason the migration guide recommends the review. The review is the mechanism that catches the behavior change that the option dropping causes.

The plugin problem. The command loads the shared and the plugin configurations, and it supports the TypeScript ESLint, the ESLint JSX A11y, the ESLint React, and the ESLint Unicorn . The plugins that Biome does not support are the ones that the command cannot migrate, and the rules from those plugins must be configured by hand . The manual configuration is the mechanism that covers the gap, and the gap is the reason the migration is not the complete for the projects with the niche plugins.

The cyclic-reference problem. Some plugins and shared configurations export objects with the cyclic references, and Biome cannot load them . The migration fails with the error, and the workaround is to comment out the plugins and the shared configurations, run the migration, and then restore them one at a time to find the responsible one . The one-at-a-time restoration is the mechanism that isolates the problem, and the isolation is the reason the migration can proceed for the rest of the configuration.


a. The command and the configuration output

The command is the three-word invocation with the --write flag. The flag is the mechanism that writes the output to the biome.json instead of printing it.

# The ESLint migration
biome migrate eslint --write

The command reads the legacy .eslintrc or the flat eslint.config.js. It follows the extends field, loads the shared and the plugin configurations, and migrates the .eslintignore . The --write flag is the mechanism that applies the output, and the command without the flag is the preview.

// The ESLint configuration
{
  "extends": ["plugin:unicorn/recommended"],
  "plugins": ["unicorn"],
  "ignore_patterns": ["dist/**"],
  "globals": {
    "Global1": "readonly"
  },
  "rules": {
    "eqeqeq": "error"
  }
}
// The migrated biome.json (simplified)
{
  "linter": {
    "rules": {
      "recommended": false,
      "suspicious": {
        "noDoubleEquals": "error"
      }
    }
  },
  "javascript": {
    "globals": ["Global1"]
  },
  "files": {
    "includes": ["!dist/**"]
  }
}

The recommended: false is the replacement mechanism, and the noDoubleEquals: "error" is the migrated rule . The ignore_patterns becomes the files.includes exception, and the globals becomes the javascript.globals array. The output is the explicit set, and the set is the translation.

The --include-inspired flag is the second option. The flag migrates the inspired rules as well as the identical ones.

# The migration with the inspired rules
biome migrate eslint --include-inspired --write

The flag is the mechanism that adds the inspired rules to the output, and the addition is the trade-off against the precision. The inspired rules may behave differently from the ESLint sources, and the difference is the reason the flag is opt-in .


b. The VCS integration and the post-migration review

The VCS integration is the separate step that the migration does not do automatically. The vcs.useIgnoreFile: true is the flag that makes Biome read the .gitignore, and the flag is the mechanism that keeps the ignore behavior consistent with ESLint .

// biome.json — the VCS integration
{
  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true
  }
}

The enabled and the clientKind are the required fields, and the useIgnoreFile is the flag that enables the .gitignore reading. The migration guide states the requirement explicitly: “ESLint, Prettier, and Stylelint skip the files in your .gitignore by default. Biome does so only when its VCS integration is enabled, so enable it to keep the same set of files ignored” . The enablement is the mechanism that keeps the ignored files ignored, and the explicit step is the reason the migration is not the complete.

The post-migration review is the manual step. The command writes the rules, and the developer checks the output against the expected behavior. The review is the mechanism that catches the known differences: the identical-versus-inspired gap, the dropped options, and the plugin rules that the command could not migrate . The review is the reason the migration is not the one-command-and-done, and the review is the step that the documentation recommends.

The removal of the ESLint dependencies is the final step. The migration guide states: “Once you are satisfied with the result, remove ESLint, Prettier, Stylelint, and their plugins from your dependencies, and replace their editor extensions with Biome’s” . The removal is the mechanism that completes the migration, and the editor extension replacement is the mechanism that keeps the format-on-save and the diagnostics working.

The editor integration is the separate concern. The VS Code settings need the editor.defaultFormatter: "biomejs.biome" and the editor.codeActionsOnSave with the source.fixAll.biome to replace the ESLint extension. The removal of the ESLint plugins is the mechanism that removes the redundant dependencies, and the eslint-config-prettier is not needed because Biome has no formatter-linter conflict .


Complete Example Session

// ============================================
// PART 1: THE ESLINT CONFIGURATION
// ============================================

// .eslintrc.json
{
  "extends": ["eslint:recommended", "plugin:react/recommended"],
  "plugins": ["react"],
  "ignorePatterns": ["dist/**", "coverage/**"],
  "globals": {
    "process": "readonly"
  },
  "rules": {
    "eqeqeq": "error",
    "no-var": "error",
    "no-console": "warn"
  }
}

// ============================================
// PART 2: THE MIGRATION COMMAND
// ============================================

// biome migrate eslint --write

// The command reads the .eslintrc.json.
// It follows the extends and loads the plugins.
// The output is the biome.json with the migrated rules.

// ============================================
// PART 3: THE MIGRATED CONFIGURATION
// ============================================

// biome.json (simplified)
{
  "linter": {
    "rules": {
      "recommended": false,
      "suspicious": {
        "noDoubleEquals": "error"
      },
      "style": {
        "noVar": "error"
      }
    }
  },
  "javascript": {
    "globals": ["process"]
  },
  "files": {
    "includes": ["!dist/**", "!coverage/**"]
  }
}

// The recommended preset is disabled.
// Only the migrated rules are enabled.

// ============================================
// PART 4: THE INCLUDED-INSPIRED FLAG
// ============================================

// biome migrate eslint --include-inspired --write

// The inspired rules are also migrated.
// The output has more rules, and the review is more important.

// ============================================
// PART 5: THE VCS INTEGRATION
// ============================================

// biome.json — the added VCS configuration
{
  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true
  }
}

// The .gitignore is respected.

// ============================================
// PART 6: THE CYCLIC REFERENCE WORKAROUND
// ============================================

// The migration fails with the cyclic reference error.
// The plugin is commented out, and the migration is rerun.
// The plugin is restored after the migration.

// ============================================
// PART 7: THE MANUAL RULE CONFIGURATION
// ============================================

// The plugin that Biome does not support is configured by hand.
// The rules are added to the biome.json manually.

// ============================================
// PART 8: THE DEPENDENCY REMOVAL
// ============================================

// npm uninstall eslint eslint-plugin-react
// The ESLint dependencies are removed after the review.

// ============================================
// PART 9: THE EDITOR REPLACEMENT
// ============================================

// The ESLint extension is disabled.
// The Biome extension is enabled.
// The editor.defaultFormatter is "biomejs.biome".

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

// [ ] The migration command is run with --write.
// [ ] The recommended preset is false.
// [ ] The migrated rules are reviewed.
// [ ] The --include-inspired flag is used if the broader set is wanted.
// [ ] The VCS integration is enabled for the .gitignore.
// [ ] The cyclic reference is handled.
// [ ] The unsupported plugin rules are configured by hand.
// [ ] The ESLint dependencies are removed.
// [ ] The editor extension is replaced.

The ten parts covered the ESLint configuration, the migration command, the migrated configuration, the --include-inspired flag, the VCS integration, the cyclic reference workaround, the manual rule configuration, the dependency removal, the editor replacement, and the checklist. The through-line is that the command ports the rules, and the review catches the differences.


Quick Reference

The migration command

CommandPurpose
biome migrate eslintThe preview
biome migrate eslint --writeThe write
biome migrate eslint --include-inspired --writeThe write with the inspired rules

The supported inputs

InputSupport
The legacy .eslintrcYes
The flat eslint.config.jsYes
The .eslintignoreYes
The YAML configurationNo

The supported plugins

PluginSupport
The TypeScript ESLintYes
The ESLint JSX A11yYes
The ESLint ReactYes
The ESLint UnicornYes

The post-migration steps

StepPurpose
Enable the VCSKeep the .gitignore behavior
Review the outputCatch the known differences
Configure the unsupported plugins by handCover the gap
Remove the dependenciesComplete the migration
Replace the editor extensionKeep the diagnostics and the format on save

Best Practices

✅ Do This:

# Use the --write flag to apply the migration
biome migrate eslint --write                                      // ✅
// Enable the VCS integration for the .gitignore
{ "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true } } // ✅

❌ Don’t Do This:

# Assume the migration is a drop-in replacement
biome migrate eslint // the known differences remain               // ❌

# Skip the VCS integration
// The .gitignore files are processed                              // ❌

Common Pitfalls

PitfallWhy It HappensFix
The rule is missingThe command migrates only the identical rulesUse the --include-inspired flag
The behavior differsThe rule is inspired, not identicalReview the rule source page
The migration failsThe plugin has the cyclic referenceComment out the plugin and rerun
The .gitignore is not respectedThe VCS integration is disabledEnable the vcs.useIgnoreFile
The rule option is ignoredThe option is not implemented in BiomeConfigure the rule by hand

Real-World Examples

1. The migration command

biome migrate eslint --write

2. The inspired rules

biome migrate eslint --include-inspired --write

3. The recommended preset

{ "linter": { "rules": { "recommended": false } } }

4. The migrated rule

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

5. The globals

{ "javascript": { "globals": ["process"] } }

6. The ignore patterns

{ "files": { "includes": ["!dist/**"] } }

7. The VCS integration

{ "vcs": { "useIgnoreFile": true } }

8. The cyclic reference

The plugin is commented out and the migration is rerun.

9. The dependency removal

npm uninstall eslint eslint-plugin-react

10. The editor replacement

{ "editor.defaultFormatter": "biomejs.biome" }

Visual

The Migration Flow

┌──────────────────────────────────────────────────────────────┐
│  THE MIGRATION FLOW                                          │
│                                                              │
│  The .eslintrc or the eslint.config.js                       │
│         │                                                    │
│         ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ biome migrate       │  The command reads the config      │
│  │ eslint --write      │  and writes the biome.json         │
│  └──────────┬──────────┘                                    │
│             │                                                │
│             ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ The biome.json with │  The recommended: false            │
│  │ the migrated rules  │  The explicit rules                │
│  └──────────┬──────────┘                                    │
│             │                                                │
│             ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ The manual review   │  The known differences             │
│  │ The VCS integration │  The plugin gap                    │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

The Identical vs Inspired Rules

┌──────────────────────────────────────────────────────────────┐
│  THE IDENTICAL VS INSPIRED                                   │
│                                                              │
│  The identical rule:                                         │
│  ┌─────────────────────┐                                    │
│  │ The same behavior   │  The migrated by default           │
│  │ The same fixes      │                                    │
│  └─────────────────────┘                                    │
│                                                              │
│  The inspired rule:                                          │
│  ┌─────────────────────┐                                    │
│  │ The similar         │  The migrated only with the        │
│  │ behavior            │  --include-inspired flag           │
│  │ The different edge  │                                    │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

The Recommended Preset

┌──────────────────────────────────────────────────────────────┐
│  THE RECOMMENDED PRESET                                      │
│                                                              │
│  Before the migration:                                       │
│  ┌─────────────────────┐                                    │
│  │ recommended: true   │  The Biome default                 │
│  └─────────────────────┘                                    │
│                                                              │
│  After the migration:                                        │
│  ┌─────────────────────┐                                    │
│  │ recommended: false  │  The migrated rules only           │
│  └─────────────────────┘                                    │
│                                                              │
│  The replacement keeps the configuration honest.            │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
The commandbiome migrate eslint --write
The inputThe .eslintrc, the eslint.config.js, the .eslintignore
The outputThe biome.json with the migrated rules
The presetThe recommended: false with the explicit rules
The identical rulesThe migrated by default
The inspired rulesThe migrated only with the --include-inspired flag
The supported pluginsThe TypeScript ESLint, the JSX A11y, the React, the Unicorn
The post-migrationThe VCS integration, the review, the dependency removal

Key takeaways:

  • The command ports the rules, the globals, the overrides, and the ignore patterns. The biome migrate eslint --write reads the ESLint configuration, follows the extends chains, loads the plugins, and writes the equivalent rules into the biome.json . The .eslintignore patterns become the files.includes exceptions.
  • The command replaces the linter settings and disables the recommended preset. It sets the recommended to false and enables only the migrated rules. The replacement is the mechanism that keeps the configuration honest, and the explicit set is the result of the translation .
  • The identical rules are migrated by default, the inspired rules only with the flag. The inspired rules may behave differently, and the --include-inspired flag is the mechanism that opts into the broader set. The review is the mechanism that catches the differences .
  • The VCS integration is the separate step. ESLint skips the .gitignore files by default, and Biome does so only when the vcs.useIgnoreFile is enabled. The enablement is the mechanism that keeps the ignored files ignored .
  • The unsupported plugins must be configured by hand. The command supports the TypeScript ESLint, the JSX A11y, the React, and the Unicorn, and the other plugins are the manual configuration. The cyclic reference is the workaround, and the one-at-a-time restoration is the isolation mechanism .

Remember: The Biome ESLint migration is the command that ports the configuration, and the review that catches the differences. The biome migrate eslint --write reads the ESLint files and writes the Biome configuration with the explicit rules. The VCS integration keeps the ignore behavior, the review catches the known differences, and the dependency removal completes the switch. The migration is the sequence, and the sequence is the complete.

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!