TL;DR
For "Biome vs OXC" and "OXC vs Biome" searches, the important distinction is scope. OXC/oxlint is the fastest lint-only pre-check for JavaScript and TypeScript projects that want Rust-speed feedback without replacing the whole toolchain. Biome is the best all-in-one formatter + linter when your project can live inside Biome's built-in rule set. ESLint remains the safest production default when framework plugins, type-aware TypeScript rules, custom rules, or ecosystem compatibility matter.
A practical 2026 setup is often not a single-tool migration:
- Use ESLint for Next.js, libraries, framework-specific plugins, custom rules, and type-aware TypeScript checks.
- Use Biome when you want one fast tool to handle formatting, import organization, and common lint rules.
- Use OXC/oxlint as a speed layer before ESLint, or as a lightweight linter for projects whose required rules are covered.
The neighboring guide Biome vs ESLint + Prettier covers the two-tool replacement question. This canonical guide stays focused on the three-way OXC vs ESLint vs Biome decision and preserves that separation.
Fast Decision Table
| Decision point | OXC / oxlint | ESLint | Biome |
|---|---|---|---|
| Primary job | Very fast linting | Extensible linting platform | Formatter + linter + import organizer |
| Best default for | Fast pre-checks, monorepos, CI acceleration | Production apps/libraries with plugin needs | Greenfield apps that want one tool |
| Formatting | No | No; pair with Prettier or another formatter | Yes |
| Plugin ecosystem | No ESLint-style custom plugin ecosystem | Largest JavaScript lint plugin ecosystem | No third-party rule plugin API |
| Type-aware TypeScript rules | Not the main value proposition | Strong with typescript-eslint | Limited compared with ESLint + TS type checker |
| Framework-specific rules | Limited | Strong: Next.js, React Hooks, Vue, Svelte, Astro, testing, security, accessibility | Partial/built-in only |
| Speed posture | Fastest for covered lint rules | Slowest cold runs, strongest coverage | Very fast and broader than lint-only |
| Migration risk | Rule gaps and config parity | Config complexity, slower CI | Plugin/type-aware gaps and formatter differences |
| Common pattern | oxlint . && eslint ... | Source of truth for specialized rules | biome check . for format+lint |
Key Takeaways
- OXC is a speed layer, not a full ESLint replacement for every team. Use it to catch common errors quickly, especially before slower ESLint/type-aware passes.
- Biome is the one-tool workflow. It is compelling when formatting, imports, and common lint rules are enough, but it is intentionally less extensible than ESLint.
- ESLint still owns long-tail correctness. If your project depends on
@typescript-eslint,eslint-plugin-react-hooks,@next/eslint-plugin-next, accessibility, security, testing, or custom organization rules, keep ESLint in the pipeline. - The best monorepo setup is usually layered. Run OXC first for quick failures, Biome only if the repo has adopted it as the formatter, and ESLint for the rules that require ecosystem depth.
- Avoid duplicate rules. If OXC or Biome catches a rule, turn off the duplicate ESLint rule unless ESLint's type-aware version is intentionally stricter.
The 2026 Linting Landscape
JavaScript linting split into three jobs:
- Formatting and imports: Prettier historically owned formatting; Biome now offers formatting and import organization in one Rust-based binary.
- Fast syntax/rule feedback: OXC/oxlint uses the Oxc parser/toolchain to make common lint checks fast enough for every save, pre-commit, or early CI step.
- Deep ecosystem correctness: ESLint still handles the long tail of plugins, framework rules, type-aware TypeScript checks, and custom organizational rules.
That means the migration question should not be "Which tool kills the others?" It should be "Which rules belong in which pass?"
OXC / oxlint: The Fast Lint Pass
OXC is the JavaScript Oxidation Compiler project. For linting, the package most teams use is oxlint.
npm install --save-dev oxlint
npx oxlint .
A minimal configuration keeps OXC focused on the rules it can enforce quickly:
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["react", "typescript"],
"rules": {
"eqeqeq": "error",
"no-console": "warn",
"no-unused-vars": "error",
"prefer-const": "error"
},
"ignorePatterns": ["dist/**", ".next/**", "coverage/**"]
}
Use OXC when:
- CI waits too long before failing on obvious lint errors;
- the monorepo has many packages and you want a fast first pass;
- common JavaScript/TypeScript/React lint rules cover most mistakes;
- you still plan to run ESLint for type-aware or framework-specific rules;
- developers need pre-commit feedback that feels instant.
Do not make OXC your only linter if you depend on custom ESLint rules, framework plugins, or TypeScript rules that require the type checker. It is best as a fast gate unless your project's rule requirements are deliberately small.
ESLint: The Extensible Source of Truth
ESLint remains the safest default for production JavaScript and TypeScript projects because it is a platform, not only a linter. It can load framework plugins, testing plugins, accessibility plugins, security plugins, custom organization rules, and typescript-eslint type-aware configs.
The modern ESLint shape is flat config:
// eslint.config.js
import js from "@eslint/js";
import tseslint from "typescript-eslint";
import reactHooks from "eslint-plugin-react-hooks";
import nextPlugin from "@next/eslint-plugin-next";
export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
{
plugins: {
"react-hooks": reactHooks,
"@next/next": nextPlugin,
},
rules: {
...reactHooks.configs.recommended.rules,
...nextPlugin.configs.recommended.rules,
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-promises": "error"
},
},
{
ignores: ["dist/**", ".next/**", "coverage/**"],
}
);
Use ESLint when:
- the app is Next.js, Vue, Svelte, Astro, React Native, Node, or another framework with important lint plugins;
- type-aware TypeScript checks protect production bugs;
- the team has custom rules or organization-specific invariants;
- accessibility/security/testing lint rules are part of quality gates;
- migration risk matters more than shaving seconds from a cold lint run.
ESLint's weakness is cold-run speed and configuration complexity. The usual fix is not to delete ESLint; it is to narrow ESLint to the rules only ESLint can enforce, then put a faster gate in front.
Biome: The All-In-One Formatter and Linter
Biome combines formatting, import organization, and linting in one tool. That is the biggest difference from OXC. OXC is about fast linting; Biome is about replacing the ESLint + Prettier + import-sorting stack for projects that can accept Biome's built-in rule set.
npm install --save-dev @biomejs/biome
npx @biomejs/biome init
npx biome check .
A typical Biome config centralizes the checks that previously lived across formatter and linter config files:
{
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"noUndeclaredVariables": "error"
},
"suspicious": {
"noExplicitAny": "warn"
},
"style": {
"useConst": "error"
}
}
}
}
Use Biome when:
- the project is greenfield or low-plugin JavaScript/TypeScript;
- one fast
biome check .command is more valuable than ESLint ecosystem breadth; - the team wants formatting, imports, and common linting in one binary;
- formatter differences from Prettier are acceptable or already tested;
- framework-specific ESLint rules are not required.
Do not switch blindly if the current ESLint config uses type-aware rules, accessibility/security plugins, framework plugins, or custom rules. Those are exactly the cases where ESLint remains valuable.
Recommended Setups by Project Type
| Project type | Recommended setup | Why |
|---|---|---|
| Next.js application | ESLint as source of truth; optionally OXC first | Next.js and React rules matter more than one-tool simplicity |
| Vite React app | Biome, or OXC + ESLint if plugins matter | Biome can replace formatter/linter for simple apps |
| TypeScript library | ESLint + typescript-eslint; optionally OXC first | Type-aware rules and API discipline matter |
| Large monorepo | OXC first, ESLint for specialized/type-aware rules | Fast failures without abandoning coverage |
| Design-system package | ESLint plus accessibility/testing plugins | Long-tail rules matter more than speed alone |
| Script-heavy repo | OXC or Biome if rules are simple | Low configuration overhead and fast feedback |
The Two-Pass Strategy: OXC Before ESLint
The most reliable production compromise is a two-pass lint pipeline:
{
"scripts": {
"lint": "oxlint . && eslint --cache .",
"lint:fast": "oxlint .",
"lint:types": "eslint --cache -c eslint.types.config.js ."
}
}
The first pass catches simple failures quickly: unused variables, equality mistakes, no-console violations, React hook mistakes that OXC supports, and import-cycle classes of problems. If it fails, CI stops before paying for the slower pass.
The second pass handles everything that requires ESLint's ecosystem: type-aware TypeScript rules, Next.js rules, accessibility, security, test rules, and custom rules.
To keep this maintainable:
- List the rules each pass owns.
- Disable duplicate ESLint rules that OXC already covers unless ESLint's type-aware version is intentionally stricter.
- Keep formatting separate unless Biome is the formatter of record.
- Track rule-gap decisions in the repo README or lint config comments.
- Re-run a full ESLint pass in CI even if local developers usually use the fast path.
Migration Paths
ESLint to OXC speed layer
npm install --save-dev oxlint
npx oxlint .
Then compare failures against ESLint. Keep OXC narrow at first, add it as lint:fast, and only promote it to the main lint script once duplicate/noisy rules are resolved.
ESLint + Prettier to Biome
npm install --save-dev @biomejs/biome
npx biome init
npx biome migrate eslint --write
npx biome migrate prettier --write
npx biome check .
Treat this as a formatter migration as well as a linter migration. Review the formatting diff separately from rule-coverage changes.
Legacy ESLint config to flat config
npx @eslint/migrate-config .eslintrc.json
Use this when ESLint remains the source of truth but the repo is still on legacy config. After the migration, simplify the config and remove rules now handled by OXC or Biome if you add them.
Common Mistakes
Replacing ESLint before checking plugin coverage
If the current repo relies on eslint-plugin-react-hooks, @next/eslint-plugin-next, eslint-plugin-jsx-a11y, eslint-plugin-security, eslint-plugin-vitest, eslint-plugin-jest, or organization custom rules, do not delete ESLint until each rule has a replacement or an accepted gap.
Running the same rule three times
A common half-migration is oxlint . && biome check . && eslint . with overlapping no-unused/no-console/style rules in every pass. That is noisy and slower than necessary. Assign ownership.
Treating Biome as only a linter
Biome's value is the combined formatter + linter workflow. If the team still uses Prettier and only wants a fast linter, OXC may be the cleaner first step.
Treating OXC as a formatter
OXC does not replace Prettier or Biome formatting. If you adopt OXC, keep your formatter decision explicit.
Internal Links for Adjacent Decisions
- For OXC's broader compiler/toolchain role, see OXC vs SWC Rust JavaScript Toolchain 2026.
- For the narrower two-way migration, see Biome vs OXC 2026.
- For ESLint/formatter replacement framing, see Biome vs ESLint + Prettier.
- Compare package health for ESLint vs Biome on PkgPulse.
Methodology and Sources
This refresh checked official documentation and current package metadata on 2026-05-16 PDT. Package versions observed from npm during the refresh were oxlint@1.65.0, eslint@10.4.0, @eslint/js@10.0.1, @biomejs/biome@2.4.15, and typescript-eslint@8.59.3.
Sources consulted:
- OXC official docs: OXC linter usage and OXC parser usage
- ESLint official docs: ESLint use guide
- Biome official docs: Biome getting started and Biome language support
- npm package metadata for
oxlint,eslint,@eslint/js,@biomejs/biome, andtypescript-eslint
Performance claims should be validated on your own repository. OXC and Biome are designed for speed and are usually much faster than a cold ESLint run, but rule coverage, TypeScript type-checking, plugin loading, file count, cache state, and CI hardware can change the measured result.
