All posts

May 6, 2026

Migrating from ESLint and Prettier to the OXC Toolchain

oxlint and oxfmt are Rust-based replacements for ESLint and Prettier. Faster feedback, simpler config, and no plugin dependency hell.


ESLint and Prettier are the defaults. They work, but they accumulate friction over time: slow CI lint steps, plugin version conflicts, cascading peer dependency warnings, and config files that grow without a clear upper bound. The OXC toolchain replaces both with Rust-based tools that are dramatically faster and far easier to configure.

What OXC Is

OXC is a suite of JavaScript tooling written in Rust. Two tools cover the ESLint and Prettier use cases:

oxlint is a linter. It covers the most common ESLint rules plus plugins for TypeScript, React, accessibility, and more. It runs in parallel across all CPU cores and produces results in milliseconds even on large codebases.

oxfmt is a formatter. It's opinionated in the same way Prettier is: one way to format, no style debates, no config file needed. Drop it in and it formats your code.

Both tools are standalone binaries. No Node.js plugin resolution, no transitive dependency tree to manage.

Why It's Worth Switching

ESLint's plugin architecture is flexible but expensive. Installing eslint-plugin-unicorn, @typescript-eslint/eslint-plugin, @typescript-eslint/parser, and their peer dependencies adds hundreds of packages to your lockfile. Each plugin needs to be compatible with the current ESLint version, which breaks on major ESLint releases.

oxlint ships with the most useful rules from those plugins built in. The TypeScript rules, the Unicorn rules, the OXC-specific rules: all available as first-party plugins with no extra installs.

Speed is the other argument. ESLint is single-threaded by default and written in JavaScript. oxlint is multi-threaded and compiled. On this project, pnpm lint completes in under 200ms. The same rules in ESLint took several seconds.

The OXC project publishes benchmark results comparing oxlint against ESLint across several real-world codebases. On the TypeScript repository (a large, representative codebase), oxlint runs in roughly 0.1 seconds where ESLint takes around 15 seconds. The speedup is consistent across codebases: typically 50 to 100 times faster. See the full numbers at oxc.rs/docs/guide/usage/linter.

The Config

oxlint reads .oxlintrc.json. The structure is straightforward:

{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "plugins": ["typescript", "unicorn", "oxc"],
  "categories": {
    "correctness": "error"
  },
  "rules": {},
  "env": {
    "builtin": true
  }
}

plugins enables rule sets. typescript covers @typescript-eslint rules, unicorn covers the unicorn ruleset, oxc adds OXC-specific correctness rules.

categories sets severity by rule category. correctness covers rules that catch real bugs: unused variables, undefined references, incorrect type assertions. Setting it to "error" fails the lint step on any violation.

Other categories you can configure: style, perf, restriction, pedantic, and nursery (unstable rules). Start with correctness and expand from there once the baseline is clean.

rules overrides specific rules. Add overrides here as you need them, same as you would in ESLint.

Migration Path

The cleanest migration is to run oxlint alongside ESLint temporarily. oxlint is fast enough that adding it to your script doesn't meaningfully slow down the lint step.

Install:

pnpm add -D oxlint oxfmt

Rather than writing .oxlintrc.json from scratch, oxlint can generate one from your existing ESLint config:

pnpm oxlint --init

This reads your current ESLint configuration and outputs an .oxlintrc.json with equivalent rules mapped across. Not every ESLint rule has an oxlint equivalent, but the ones that do are carried over automatically. Rules without a match are listed so you know what coverage gaps remain.

Add scripts to package.json:

{
  "scripts": {
    "lint": "oxlint",
    "lint:fix": "oxlint --fix",
    "fmt": "oxfmt --check",
    "fmt:fix": "oxfmt --write"
  }
}

Run pnpm lint and see what violations come up. Most are genuine issues. Fix them, then delete your ESLint config, eslint package, and all ESLint plugins from package.json. Do the same for Prettier: run pnpm fmt:fix once to reformat the codebase, then delete .prettierrc and the prettier package.

The only thing that doesn't carry over is custom ESLint rules written as plugins. If you have those, evaluate whether oxlint covers the same ground before removing ESLint.

The official migration guide covers rule mapping and edge cases in detail: Migrate from ESLint.

Formatting Without Config

oxfmt requires no configuration file. It formats TypeScript, JavaScript, JSX, and TSX with a single opinionated style. The output is stable: running it twice produces the same result, so CI can verify formatting without any special setup.

Format everything:

pnpm fmt:fix

Check formatting in CI:

pnpm fmt

--check exits with a non-zero code if any file would be reformatted. That's the only output CI needs.

For edge cases around Prettier option mapping: Migrate from Prettier.

CI Integration

The lint and format check fit naturally into a CI step before deployment:

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with:
          version: 10
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - run: pnpm install --frozen-lockfile
      - run: pnpm fmt
      - run: pnpm lint
      - run: pnpm test

Both steps complete in under a second on a warm runner. The total overhead added to CI is negligible.


ESLint and Prettier solved real problems when they were introduced. The OXC toolchain solves the same problems with less configuration and a fraction of the runtime. If your lint step is slow or your ESLint config has grown past the point where you understand all of it, this migration is worth the hour it takes.