OXC vs ESLint vs Biome: JavaScript Linting in 2026
TL;DR
The linting space is in flux in 2026. ESLint v9 with flat config is the universal default — 50M+ weekly downloads, every framework supports it. Biome is the fastest all-in-one linter+formatter at near-zero config, and a compelling choice for greenfield projects. OXC (oxlint) is the newest entrant — a Rust-based linter that's 50-100x faster than ESLint — but lacks the rule ecosystem. Most teams should use ESLint v9 or Biome; OXC is worth watching but not production-primary yet.
Key Takeaways
- ESLint v9 introduced flat config (
eslint.config.js) — the old.eslintrcformat is officially deprecated - Biome v1.9+ handles formatting AND linting in one tool (replaces ESLint + Prettier)
- OXC (
oxlint) is 50-100x faster than ESLint but covers ~300 rules vs ESLint's 700+ - Biome has ~200 lint rules (growing), zero JavaScript, written in Rust
- TypeScript support: Biome has first-class TS support; OXC has TS parsing; ESLint needs
@typescript-eslint - Ecosystem: ESLint has 4000+ plugins; Biome is standalone; OXC ecosystem is nascent
ESLint v9: Flat Config Migration
ESLint crossed 50M weekly downloads in 2026. The biggest change in v9 is the flat config system — .eslintrc.* is gone, replaced by eslint.config.js:
Old Way (.eslintrc.json — deprecated)
{
"env": { "browser": true, "es2022": true },
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module",
"project": "./tsconfig.json"
},
"rules": {
"no-console": "warn",
"@typescript-eslint/no-unused-vars": "error"
}
}
New Way (eslint.config.js — v9 default)
// eslint.config.js
import globals from "globals";
import js from "@eslint/js";
import tseslint from "typescript-eslint";
export default [
// Built-in recommended rules
js.configs.recommended,
// TypeScript support
...tseslint.configs.recommended,
// Global settings
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
parserOptions: {
project: "./tsconfig.json",
},
},
},
// Your custom rules
{
rules: {
"no-console": "warn",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
},
},
// Ignores (replaces .eslintignore)
{
ignores: ["dist/**", "node_modules/**", "coverage/**", "*.config.js"],
},
];
ESLint v9 with React
// eslint.config.js
import js from "@eslint/js";
import tseslint from "typescript-eslint";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
{
plugins: {
"react-hooks": reactHooks,
"react-refresh": reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
},
}
);
ESLint Performance (2026 Benchmarks)
ESLint is JavaScript, not Rust. For large codebases, it's slow:
| Codebase size | ESLint v9 | Biome | OXC |
|---|---|---|---|
| 100 files | 1.2s | 0.08s | 0.03s |
| 500 files | 5.8s | 0.3s | 0.1s |
| 2000 files | 24s | 1.1s | 0.4s |
ESLint supports --cache to skip unchanged files, but cold runs are slow.
Biome: The All-In-One Alternative
Biome was born from Rome Tools' pivot — it's a Rust-based formatter + linter + import sorter that replaces ESLint + Prettier in a single binary.
npm install --save-dev @biomejs/biome
npx @biomejs/biome init
biome.json
{
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
"organizeImports": { "enabled": true },
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"noUndeclaredVariables": "error",
"useExhaustiveDependencies": "error"
},
"suspicious": {
"noExplicitAny": "warn",
"noDoubleEquals": "error"
},
"style": {
"useConst": "error",
"noNonNullAssertion": "warn"
}
}
},
"javascript": {
"parser": {
"unsafeParameterDecoratorsEnabled": false
},
"formatter": {
"quoteStyle": "double",
"trailingCommas": "es5"
}
}
}
Biome CLI
# Lint and format together
biome check --write .
# Just lint (no formatting)
biome lint .
# Just format
biome format --write .
# CI mode (no changes, just error on violations)
biome ci .
Biome's Rule Coverage
Biome v1.9 ships ~200 lint rules across:
correctness— logic errors, exhaustive deps, unused variablessuspicious— double equals, commented code, console.logstyle— const vs let, arrow functions, template literalsnursery— experimental rules (opt-in)performance— no-barrel-files, no-array-index-key
What Biome doesn't have: Biome has no plugin system. If you need custom rules, Biome can't do it. ESLint's plugin ecosystem (4000+ packages) is irreplaceable for complex projects.
Biome vs Prettier: Compatibility
Biome formats identically to Prettier for ~97% of cases. The remaining 3% are edge cases where Biome made different aesthetic choices. The biome migrate prettier command handles the gap:
npx @biomejs/biome migrate prettier --write
# Reads .prettierrc and applies those settings to biome.json
OXC: Rust-Speed Linting
OXC (JavaScript Oxidation Compiler) is the Rust-based JavaScript/TypeScript toolchain that includes:
- oxc_parser — the fastest JS/TS parser in the ecosystem
- oxlint — linter built on oxc_parser (the installable npm package)
- oxc_transformer — TS/JSX transformer (Babel replacement)
- rolldown uses oxc_parser internally
npm install oxlint --save-dev
npx oxlint .
OXC Speed
OXC's headline claim is 50-100x faster than ESLint. From their benchmarks:
| Command | ESLint | OXC |
|---|---|---|
| Lint TypeScript (cold) | 24.0s (2000 files) | 0.4s |
| Lint React codebase | 9.2s (800 files) | 0.18s |
This is achieved by being written in Rust and using multithreaded parsing.
OXC Configuration
// .oxlintrc.json
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"rules": {
"no-unused-vars": "error",
"no-console": "warn",
"eqeqeq": "error",
"prefer-const": "error"
},
"plugins": ["react", "typescript"],
"env": {
"browser": true,
"node": true
},
"ignorePatterns": ["dist/**", "node_modules/**"]
}
OXC Rule Coverage
OXC supports ~300 rules including ESLint core rules, React hooks rules, and TypeScript rules. The coverage is growing rapidly, but there are gaps:
OXC has:
- ✅ All ESLint core recommended rules
- ✅ React hooks rules (
react-hooks/rules-of-hooks,react-hooks/exhaustive-deps) - ✅ Common TypeScript rules
- ✅ Import/no-cycle (graph-based, very fast)
OXC doesn't have:
- ❌
@typescript-eslintadvanced type-aware rules (requires type checking) - ❌ Custom plugin system
- ❌ Most framework-specific plugins (Svelte, Vue, Astro)
- ❌
eslint-plugin-security,eslint-plugin-sonarjs
OXC as a Speed Layer
A popular pattern is running OXC first (fast, catches obvious issues) and ESLint only for type-aware rules:
// package.json
{
"scripts": {
"lint": "oxlint . && eslint --cache . --rule '@typescript-eslint/no-floating-promises: error'",
"lint:fast": "oxlint .",
"lint:types": "eslint --no-eslintrc -c eslint.types.config.js ."
}
}
Framework-Specific Recommendations
Next.js Project
// eslint.config.js — Next.js uses ESLint by default
import nextPlugin from "@next/eslint-plugin-next";
export default [
...tseslint.configs.recommended,
{
plugins: { "@next/next": nextPlugin },
rules: {
...nextPlugin.configs.recommended.rules,
...nextPlugin.configs["core-web-vitals"].rules,
},
},
];
Next.js's eslint-config-next has not been ported to Biome. Use ESLint for Next.js projects.
Vite + React (Biome is a great fit)
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install --save-dev @biomejs/biome
npx @biomejs/biome init
# Remove eslint and prettier from devDependencies
Biome replaces ESLint + Prettier perfectly for Vite/React apps without framework-specific lint rules.
Library Development
Use ESLint. Libraries often need:
@typescript-eslinttype-aware ruleseslint-plugin-jestoreslint-plugin-vitesteslint-plugin-jsdocfor API documentation- Custom rules for your library's API patterns
Feature Comparison Table
| Feature | ESLint v9 | Biome | OXC |
|---|---|---|---|
| Speed | ⚠️ Slow (JS) | ✅ Fast (Rust) | ✅ Fastest (Rust) |
| Formatting | ❌ (needs Prettier) | ✅ Built-in | ❌ |
| Import sorting | 🔌 Plugin needed | ✅ Built-in | ❌ |
| Rule count | ✅ 700+ core | ✅ ~200 | ⚠️ ~300 |
| Plugin ecosystem | ✅ 4000+ plugins | ❌ None | ❌ None |
| Type-aware rules | ✅ Full TS support | ⚠️ Limited | ❌ |
| Custom rules | ✅ Full JS API | ❌ | ❌ |
| Vue/Svelte/Astro | ✅ Via plugins | ⚠️ Partial | ❌ |
| Config complexity | ⚠️ Moderate | ✅ Minimal | ✅ Minimal |
| Auto-fix | ✅ | ✅ | ✅ |
| Editor integration | ✅ Universal | ✅ VSCode/JetBrains | ⚠️ Growing |
| Weekly downloads | 50M+ | 3M+ | Growing |
Migration Paths
.eslintrc → ESLint v9 Flat Config
# Official migration helper
npx @eslint/migrate-config .eslintrc.json
This generates an eslint.config.js from your old config. Usually requires manual cleanup.
ESLint + Prettier → Biome
npx @biomejs/biome migrate eslint --write
npx @biomejs/biome migrate prettier --write
Two commands convert your existing configs. Then uninstall ESLint and Prettier.
Recommended Setup by Project Type
| Project | Recommendation | Why |
|---|---|---|
| Next.js app | ESLint v9 + next plugin | Next.js requires ESLint; flat config is clean |
| Vite + React | Biome | No framework-specific rules needed; Biome is faster |
| Node.js library | ESLint v9 | Type-aware rules critical for library quality |
| Monorepo | OXC (fast CI) + ESLint (type rules) | Best of both worlds |
| New SvelteKit project | ESLint + svelte plugin | Biome/OXC don't support Svelte syntax yet |
| Legacy Webpack project | ESLint v9 (migrate to flat config) | Stability over speed |
Methodology
- Benchmarked on a 2000-file TypeScript React codebase (MacBook M3 Pro + GitHub Actions ubuntu-latest)
- Tested ESLint v9.18, Biome v1.9.3, OXC 0.14.x
- Reviewed OXC roadmap for rule implementation status
- Tested all flat config migration paths with official migration tools
- Analyzed npm download trends for eslint, @biomejs/biome, and oxlint packages
See ESLint vs Biome download trends on PkgPulse — updated in real time.
See the live comparison
View eslint vs. prettier on PkgPulse →