Oxlint vs ESLint: Why Large Repos Run Both
Oxlint runs 50 to 100 times faster than ESLint and ships more than 865 rules. It is still not a full replacement, because JS plugin support is alpha. The pattern that actually works today is running both.
Oxlint is a linter for JavaScript and TypeScript built on the Oxc compiler stack in Rust. Its headline claim is that it runs 50 to 100 times faster than ESLint, and unlike most performance claims in this space it is not marginal: it is the difference between a lint step you wait for and one you do not notice. Elastic Kibana, Sentry, and Cloudflare run it in production.
The question everyone actually asks is whether it replaces ESLint. As of 2026 the honest answer is not yet, and the reason is narrow and specific: oxlint supports native plugins and JS plugins compatible with the ESLint plugin ecosystem, but the JS plugin support is still alpha. If your config depends on a custom in-house rule or a niche community plugin, that is the thing that will stop a clean migration, not rule count and not speed.
So the useful comparison is not "which one wins". It is which parts of your lint config can move today, what running both costs you, and what has to be true before a full migration makes sense. ESLint v10 with flat config is the reference point throughout.
Oxlint vs ESLint at a glance
| Oxlint | ESLint | |
|---|---|---|
| Implementation | Rust, on the Oxc compiler stack | JavaScript |
| Speed | 50-100x faster (vendor benchmark) | Baseline |
| Rules | 865+ including core, TypeScript, React, Jest, Vitest | Core plus the full plugin ecosystem |
| Custom JS plugins | Alpha | Mature, the ecosystem standard |
| Native plugins | Yes | Not applicable |
| Type-aware linting | Yes, via tsgo (TypeScript 7) | Yes, via typescript-eslint |
| Config format | Own config, ESLint-like | Flat config (eslint.config.js) in v9 and v10 |
| Autofix | Yes (--fix) | Yes (--fix) |
| Editor integration | Available | Universal |
| Drop-in replacement | Not yet | n/a |
What 50-100x actually buys you in CI
Speed multipliers are easy to dismiss until you convert them to the thing you actually pay for, which is wall-clock time on every push and every pull request.
| ESLint lint step | Oxlint at 50x | Oxlint at 100x |
|---|---|---|
| 10 seconds | 0.2 s | 0.1 s |
| 60 seconds | 1.2 s | 0.6 s |
| 3 minutes | 3.6 s | 1.8 s |
| 8 minutes (large monorepo) | 9.6 s | 4.8 s |
Rule coverage: 865+ rules, and the gap that matters
Oxlint ports ESLint core rules plus TypeScript rules and popular plugin rule sets including React, Jest, and Vitest. For a mainstream config that is most of what a typical eslint.config.js enables.
- What ports cleanly: core correctness rules, TypeScript rules, React hooks and JSX rules, and the common test-framework plugins.
- What does not: your own in-house rules, and smaller community plugins nobody has ported.
- Rule *names* mostly match ESLint, so disable comments and config intent carry over more easily than a rewrite would suggest.
Type-aware linting is the newest and biggest change
Type-aware rules are the ones that need the type checker rather than just the syntax tree: floating promises, unsafe any propagation, misused promises. Historically these were ESLint-only via typescript-eslint, and they were also the slowest rules in any config because they require a full type-check.
- Oxlint now supports type-aware linting by integrating with the TypeScript Go port (tsgo, TypeScript 7), enabling checks such as floating promise detection.
- This removes what used to be the strongest technical reason a TypeScript codebase could not consider oxlint at all.
- It is also the area moving fastest, so verify current coverage against the rules you actually depend on rather than assuming parity.
The real blocker: custom plugins are alpha
If you maintain in-house rules, this is the whole decision. Oxlint supports JS plugins compatible with the ESLint plugin ecosystem, but that support is documented as alpha. Alpha is fine for a lint experiment and not fine for the gate that blocks your merges.
- A repo with no custom rules and mainstream plugins can often migrate today.
- A repo with in-house rules that encode architectural constraints should keep ESLint for those rules specifically.
- Native (Rust) plugins are stable but mean writing Rust, which is a different proposition from maintaining a JS rule.
The pattern large repos actually use: both
Rather than choosing, run oxlint as a fast pre-filter and ESLint for the rules oxlint cannot yet cover. You get the fast feedback loop where it matters and keep full rule coverage where you need it.
# package.json scripts
{
"scripts": {
"lint:fast": "oxlint",
"lint:full": "eslint .",
"lint": "oxlint && eslint ."
}
}
# .github/workflows/ci.yml
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22, cache: npm }
- run: npm ci
# Fails in about a second on the 865 rules oxlint covers,
# so the slow full pass only runs on code that already passes.
- run: npx oxlint
- run: npx eslint .When a full migration makes sense
- Inventory your ESLint plugins and custom rules, and check each against the oxlint rule list.
- If nothing custom appears, run
oxlintalongside ESLint in CI for a couple of weeks and diff the findings. - Investigate every disagreement. Rules with the same name can differ at the edges, and those edges are where a silent behaviour change hides.
- Move the fast pass into a pre-commit hook once you trust it, so lint failures stop reaching CI at all.
- Retire ESLint only when the diff is empty and nothing in your config depends on alpha JS plugin support.
The switching cost is mostly in the parts nobody lists
- Assertions and mocks usually port mechanically when the target implements a compatible API; custom transformers and framework plugins do not.
- Snapshot formats differ between runners, so plan to regenerate and review rather than port.
- Run both suites in parallel in CI for a period and diff the results. A migration that changes which tests fail is not a migration, it is a regression you have not found yet.
- Coverage numbers move on a runner change even when the tests do not, because instrumentation differs. Re-baseline any coverage gate deliberately.
The verdict
Do not frame this as a replacement decision yet. Oxlint is dramatically faster, covers 865+ rules including TypeScript, React, and the common test plugins, and now does type-aware linting via tsgo. That is enough to be genuinely useful today.
It is not enough to retire ESLint in a repo with in-house rules, because JS plugin support is still alpha and that is exactly the part your custom rules depend on.
Run both, oxlint first. You get sub-second feedback on the vast majority of violations and keep full coverage from ESLint, and the migration path stays open for whenever plugin support stabilises. That is the configuration most large adopters are actually running.
Frequently asked questions
Is oxlint a drop-in replacement for ESLint?
How much faster is oxlint than ESLint?
Does oxlint support type-aware linting?
How many rules does oxlint have?
Can I run oxlint and ESLint together?
oxlint first so the majority of violations surface in about a second, then run eslint . for the rules oxlint does not cover. Failure latency improves with no loss of coverage.Who uses oxlint in production?
Does oxlint support autofix?
oxlint --fix, the same shape as eslint --fix. Because it is so much faster, autofix on save is practical in a way a slow ESLint pass often is not.