<!-- PkgPulse AI-readable guide source -->
<!-- Canonical: https://www.pkgpulse.com/guides/oxc-vs-eslint-vs-biome-javascript-linting-2026 -->
<!-- Raw Markdown: https://www.pkgpulse.com/guides/oxc-vs-eslint-vs-biome-javascript-linting-2026/raw.md -->
<!-- Source path: content/guides/oxc-vs-eslint-vs-biome-javascript-linting-2026.mdx -->

---
og_image: "/images/guides/oxc-vs-eslint-vs-biome-javascript-linting-2026.webp"
title: "OXC vs ESLint vs Biome: JavaScript Linting in 2026"
description: "Compare OXC, ESLint, and Biome for JavaScript linting in 2026: speed, rule coverage, formatting, plugins, TypeScript, and migration paths."
date: "2026-03-09"
author: "PkgPulse Team"
tags: ["eslint", "biome", "oxc", "linting", "tooling", "javascript", "2026"]
featured_comparison: "eslint-vs-prettier"
tier: 2
---

## 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](/guides/biome-vs-eslint-prettier-linting-2026) 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:

1. **Formatting and imports:** Prettier historically owned formatting; Biome now offers formatting and import organization in one Rust-based binary.
2. **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.
3. **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`.

```bash
npm install --save-dev oxlint
npx oxlint .
```

A minimal configuration keeps OXC focused on the rules it can enforce quickly:

```json
{
  "$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:

```javascript
// 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.

```bash
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:

```json
{
  "$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:

```json
{
  "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:

1. List the rules each pass owns.
2. Disable duplicate ESLint rules that OXC already covers unless ESLint's type-aware version is intentionally stricter.
3. Keep formatting separate unless Biome is the formatter of record.
4. Track rule-gap decisions in the repo README or lint config comments.
5. Re-run a full ESLint pass in CI even if local developers usually use the fast path.

---

## Migration Paths

### ESLint to OXC speed layer

```bash
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

```bash
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

```bash
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](/guides/oxc-vs-swc-rust-javascript-toolchain-2026).
- For the narrower two-way migration, see [Biome vs OXC 2026](/guides/biome-vs-oxc-2026).
- For ESLint/formatter replacement framing, see [Biome vs ESLint + Prettier](/guides/biome-vs-eslint-prettier-linting-2026).
- Compare package health for [ESLint vs Biome](/compare/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](https://oxc.rs/docs/guide/usage/linter.html) and [OXC parser usage](https://oxc.rs/docs/guide/usage/parser.html)
- ESLint official docs: [ESLint use guide](https://eslint.org/docs/latest/use/)
- Biome official docs: [Biome getting started](https://biomejs.dev/guides/getting-started/) and [Biome language support](https://biomejs.dev/internals/language-support/)
- npm package metadata for `oxlint`, `eslint`, `@eslint/js`, `@biomejs/biome`, and `typescript-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.
