CodeWorlds
Back to collections
Guide15 min readCodeWorlds Team

Oxlint, a fast JS and TS linter written in Rust

Oxlint checks JavaScript and TypeScript in milliseconds. Version 1.79.0, 870 rules, type-aware mode, and what it still cannot take away from ESLint.

Oxlint, a fast JS and TS linter written in Rust

Oxlint is a linter for JavaScript and TypeScript written in Rust, part of the Oxc project. Version 1.79.0 was published on 18 August 2026 under the MIT license. On the src directory of this repository, that is 643 files, a run with the default configuration takes about 0.04 seconds on my eight-core laptop.

Oxc is a suite of tools, oxlint is one of them

The names Oxc and oxlint get used interchangeably, and that is the first source of installation mistakes. Oxc, the Oxidation Compiler, is an umbrella over several independent programs: a parser, a transformer, a minifier, a module resolver, a formatter and the linter. They all live in the single oxc-project/oxc repository, but they reach the npm registry as separate packages with their own version numbers.

The state at the time of writing looks like this. The linter is the oxlint package at version 1.79.0. The formatter is oxfmt at version 0.64.0, first published on 10 September 2025, so still short of a one. The parser, transformer and minifier are oxc-parser, oxc-transform and oxc-minify, all at version 0.146.0. The resolver is oxc-resolver at version 11.24.2. On top of that sit @oxc-project/types with syntax tree node definitions and @oxc-project/runtime with helpers for the transformer, both at version 0.146.0. There is no single version for the whole suite, so the question "which Oxc version do you have" has no sensible answer.

The nastiest naming trap sits in the npm registry itself. A package called oxc exists, carries version 1.0.1, dates from 5 May 2016 and points at the jasonmccreary/oxc repository. It is a command line tool for opening projects in Xcode and has nothing to do with the Oxidation Compiler. Typing npm install oxc installs something entirely different from what you expect, and you get no warning about it.

The linter binary itself is also not where you might assume. The oxlint package contains JavaScript code only, and the binary arrives through nineteen optional dependencies named @oxlint/binding-darwin-arm64, @oxlint/binding-linux-x64-gnu, @oxlint/binding-win32-x64-msvc and so on. An install that skips optional dependencies produces a package that will not run at all.

Version, license and how it is distributed

I checked the license in three places, because packages in this collection have already turned out to disagree with themselves.

The first source is the LICENSE file on the main branch of the repository. It holds the MIT text with two notices: "Copyright (c) 2024-present VoidZero Inc. & Contributors" and "Copyright (c) 2023 Boshen". The second notice is a trace of the period when a single author ran the project, before the company existed.

The second source is the license field in the npm registry for the oxlint package, which reads MIT. The third and most important is the content of the published archive. The oxlint-1.79.0.tgz file weighs 371 kilobytes compressed and about 2.38 megabytes unpacked, holds seventeen files including a LICENSE carrying the MIT text, a dist directory with real code, the bin/oxlint entry point and a configuration_schema.json of roughly 758 kilobytes. This is neither a stub nor an empty metapackage: the code is there and so is the license file. I also spot-checked one binary package, @oxlint/binding-linux-x64-gnu at version 1.79.0, and it declares MIT as well. All three sources say the same thing, which in this collection is more of an exception than a rule.

The release rhythm is dense. The package has existed in the registry since 27 June 2023, reached version 1.0.0 on 10 June 2025, and 206 versions have been published to date. The last five releases fell on 21 July, 27 July, 3 August, 10 August and 18 August 2026, roughly one per week. The middle number rises with every release, so a dependency written as ^1.79.0 will accept hundreds of new rules without asking.

The engines field requires Node at ^20.19.0 || >=22.12.0. That is a real constraint rather than a formality, and it will break installs on the older container images still circulating on build servers.

The declared peer dependencies are worth a look too, because they say something about the project's direction. There are two, both marked optional: oxlint-tsgolint at version 7.0.2001 or later, and vite-plus with no version bound. The first is the component behind rules that use type information. The second is vite-plus, a package at version 0.2.9 described as a unified toolchain, a product of VoidZero, the same company behind Vite and Vitest. The migration documentation openly points some readers that way: oxlint for dedicated linting, Vite+ for an integrated workflow. The linter is MIT and nobody can take it away from you, but the direction of development is set by a company selling a wider product, and that is a risk worth naming when you pick a tool.

Installation and the first run

Oxlint needs no configuration to do something useful. Run without a settings file it enables the correctness category and three default plugins.

Code
Bash
npm install --save-dev oxlint@1.79.0
npx oxlint src
npx oxlint --init
npx oxlint --print-config
npx oxlint src --fix
npx oxlint src --format=github --deny-warnings

The --init command creates an .oxlintrc.json with default values. The --print-config command prints the full, expanded configuration as JSON and is the best way to check what is actually enabled instead of guessing from the documentation.

There are three fixing flags and they differ in risk. --fix applies changes considered safe, --fix-suggestions adds suggestions that may change program behaviour, and --fix-dangerously also covers those marked dangerous. On a build server you normally use the first one only, or none.

The output formats are checkstyle, default, agent, github, gitlab, json, junit, sarif, stylish and unix. The agent format targets coding assistants, github targets annotations on a change request.

Configuration and the fields that actually exist

An .oxlintrc.json accepts exactly twelve top-level fields: $schema, categories, env, extends, globals, ignorePatterns, jsPlugins, options, overrides, plugins, rules and settings. Anything outside that list is rejected.

Code
JSON
{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "plugins": ["eslint", "typescript", "unicorn", "oxc", "import", "react", "vitest"],
  "categories": {
    "correctness": "error",
    "suspicious": "warn",
    "pedantic": "off"
  },
  "env": { "browser": true, "es2024": true },
  "globals": { "__DEV__": "readonly" },
  "ignorePatterns": ["dist", "coverage", "**/*.generated.ts"],
  "options": {
    "denyWarnings": false,
    "maxWarnings": 50,
    "reportUnusedDisableDirectives": true,
    "respectEslintDisableDirectives": true,
    "typeAware": false,
    "typeCheck": false
  },
  "rules": {
    "eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
    "typescript/no-explicit-any": "warn",
    "import/no-cycle": "error"
  },
  "overrides": [
    {
      "files": ["**/*.test.ts", "**/*.test.tsx"],
      "excludeFiles": ["**/fixtures/**"],
      "plugins": ["vitest"],
      "rules": { "vitest/expect-expect": "error" }
    }
  ]
}

There are seven categories and each takes the value allow, off, warn, error or deny: correctness, suspicious, pedantic, perf, style, restriction and nursery. The all shorthand available on the command line covers every one of them except nursery.

The plugins field takes values from a closed list of fifteen names: eslint, react, unicorn, typescript, oxc, import, jsdoc, jest, vitest, jsx-a11y, nextjs, react-perf, promise, node, vue. Setting this field overwrites the default set, which is unicorn, typescript and oxc, rather than adding to it. That is the most common cause of a situation where adding a single plugin makes a batch of rules quietly stop reporting.

An overrides entry accepts the fields files, excludeFiles, env, globals, jsPlugins, plugins and rules. The typeAware and typeCheck fields from the options section work in the root configuration only and are ignored in nested ones.

The alternative is an oxlint.config.ts file, marked experimental in the documentation and requiring a Node runtime.

Code
TypeScript
import { defineConfig } from "oxlint";

export default defineConfig({
  plugins: ["typescript", "unicorn", "oxc"],
  options: {
    typeAware: true,
    typeCheck: true
  },
  rules: {
    "typescript/no-floating-promises": ["error", { ignoreVoid: true }],
    "typescript/no-unsafe-assignment": "warn"
  }
});

The extends field differs between the two as well. In .oxlintrc.json it is a list of path strings resolved relative to the file containing them. In oxlint.config.ts it is a list of imported configuration objects. Copying one straight into the other will not work.

How many rules oxlint really has

Rather than repeat a number from the project's own materials, I counted it with --print-config on version 1.79.0.

Code
Bash
npx oxlint --print-config | node -e "
  let s=''; process.stdin.on('data', d => s += d).on('end', () => {
    console.log(Object.keys(JSON.parse(s).rules).length)
  })"

npx oxlint --print-config -D all -D nursery \
  --import-plugin --react-plugin --jsdoc-plugin --jest-plugin \
  --vitest-plugin --jsx-a11y-plugin --nextjs-plugin --react-perf-plugin \
  --promise-plugin --node-plugin --vue-plugin > full.json

With no configuration, 111 rules are enabled. With every category including nursery and every plugin turned on, the count is 870 rules, distributed as follows.

NamespaceRule countCounterpart in the ESLint world
eslint187ESLint core rules
unicorn138eslint-plugin-unicorn
typescript110typescript-eslint
react85react, react-hooks, react-refresh, React Compiler
vitest73@vitest/eslint-plugin
jest60eslint-plugin-jest
vue46eslint-plugin-vue, script section only
jsx_a11y36eslint-plugin-jsx-a11y
import33eslint-plugin-import
oxc27Oxc-specific rules and ports from deepscan
jsdoc23eslint-plugin-jsdoc
nextjs21@next/eslint-plugin-next
promise16eslint-plugin-promise
node11eslint-plugin-n
react_perf4eslint-plugin-react-perf

The table shows both the reach and its limits. Fourteen built-in plugins cover the rule sets you meet most often, and outside them nothing is native. There are 110 rules for TypeScript and they do not cover the whole of typescript-eslint, while the Vue plugin only handles rules that operate on the script section, so template analysis is out.

Rules that need type information are a separate story. They are handled by the oxlint-tsgolint component, version 7.0.2001 from 21 July 2026, MIT licensed, written in Go and built on typescript-go. The documentation reports coverage of 59 out of 61 type-aware rules from typescript-eslint. The price is concrete: TypeScript 7.0 or newer is required, some older tsconfig.json options do not work including baseUrl, and for very large repositories the documentation itself warns about high memory use.

Code
Bash
npm install --save-dev oxlint-tsgolint@7.0.2001
npx oxlint --type-aware
npx oxlint --type-aware --type-check
npx oxlint --type-aware --debug timings
OXC_LOG=debug npx oxlint --type-aware

The --type-check mode reports type errors alongside lint results and can replace a separate tsc --noEmit step on a build server. That is an unassuming but large difference from Biome, which does no type checking at all.

For plugins that have no native port there is the jsPlugins mechanism, compatible with the ESLint version nine API. It is marked alpha and has two clear gaps: no support for custom parsers, meaning Svelte, Vue and Angular files, and no support for rules that use types. Rules from such plugins run slower, because they pass through a JavaScript layer, which is exactly what oxlint set out to avoid.

Living alongside ESLint

Oxlint is not a replacement for ESLint in the "remove one, drop in the other" sense. The project documentation says so plainly and recommends a two-stage arrangement.

Code
Bash
npx @oxlint/migrate
npx @oxlint/migrate --type-aware
npx @oxlint/migrate --js-plugins=false
npm install --save-dev eslint-plugin-oxlint@1.79.0
npx oxlint && npx eslint .

The @oxlint/migrate tool at version 1.79.0 reads an ESLint flat configuration from version nine or ten and generates an .oxlintrc.json, preserving severities, rule options, path-specific overrides and global variables. Old .eslintrc.js files will not pass through directly, they have to go through @eslint/migrate-config first. Local plugins from your own repository are not carried over automatically and have to be added by hand to the jsPlugins field.

The eslint-plugin-oxlint package, also at version 1.79.0, does the reverse: it switches off the ESLint rules that oxlint already checks. Without it you get every problem reported twice, and the total run time does not drop at all.

Order matters. Oxlint running first rejects a change in tens of milliseconds, so ESLint only starts on code that already passed the cheap stage. The reverse order gains nothing. If you build with Turborepo, it pays to keep both runs as separate tasks so the cache treats them independently.

Oxlint against Biome

Biome is the closest competitor and the choice between them comes down to one decision: do you want a single tool for formatting and linting, or two separate ones.

TraitOxlint 1.79.0Biome 2.5.9
Scopelinting onlyformatting, linting, source actions
Formattingseparate oxfmt package 0.64.0built in, mature
LicenseMITMIT or Apache 2.0, your choice
Custom rulesJavaScript plugins on the ESLint v9 API, alphaGritQL in .grit files only
Type-aware rules59 of 61 via oxlint-tsgolint, needs TypeScript 7own inference, no tsc
Type checking--type-check replaces tsc --noEmitnone, tsc stays in the pipeline
Languages beyond JS and TSnoneCSS, GraphQL, JSON, early Vue and Svelte
Configuration file.oxlintrc.json or oxlint.config.tsbiome.json

The practical conclusion runs like this. If you already have Prettier and are happy with it, and only want to cut linting time, oxlint fits better because it does not try to rewrite your whole repository along the way. If you want to drop the ESLint plus Prettier pair in one move, Biome does that more completely today, because its formatter is past version two while oxfmt has only just passed zero. If rules that use type information matter to you, oxlint in --type-aware mode is closer to the goal, provided you can raise TypeScript to version seven.

Both tools share an area neither touches: they do not build applications, which is what esbuild or Vite are for, and they have nothing to do with the data layer, handled by something like Prisma.

Two neighbouring categories are worth separating, because the boundary blurs. A linter watches style and common errors, while Semgrep matches patterns against the syntax tree and hunts for vulnerabilities, so a rule survives a change of formatting or variable name. Before adopting it, though, check where the paid boundary runs: cross-file and cross-function analysis is not in the open source but in a closed binary downloaded after login, and the community rule set carries a bespoke licence forbidding redistribution. Free and open are two different things there.

Common mistakes during adoption

The first is installing the oxc package instead of oxlint. You end up with an unrelated program from 2016 and no warning whatsoever.

The second is setting the plugins field expecting it to add to the defaults. It overwrites them. The list has to name every plugin you want, including unicorn, typescript and oxc if they are to stay.

The third is carrying over a rule that oxlint does not implement. The configuration does not warn in that case, it refuses to load: the message reads "Failed to parse oxlint configuration file" and names the rule unknown in that plugin. Rewriting a configuration by hand, line by line, ends in a series of such surprises, which is why it is better to run @oxlint/migrate, which simply drops unsupported rules.

The fourth is installing with the flag that skips optional dependencies. Without the @oxlint/binding-* package matching your system there is nothing to run.

The fifth is a Node older than 20.19. The engines field is a real constraint here, not an aspirational note.

The sixth is setting typeAware or typeCheck in a nested configuration. Both fields work in the root file only and will be ignored in a subdirectory without a word of explanation.

The seventh is running --type-aware on a repository whose root tsconfig.json sets include to **/*. That produces one enormous program covering build outputs as well, and the run stretches into minutes. Diagnosing it is simple: with OXC_LOG=debug the log shows a line with the number of source files in the program.

The eighth is expecting oxlint to format code. It will not, because that is the job of oxfmt, a separate package and a separate decision.

The ninth is enabling the nursery category without pinning an exact version. Rules in that group change names and behaviour between releases, and releases arrive weekly.

FAQ

Will oxlint replace ESLint in my project?

It depends on the configuration. If it rests on core rules, typescript-eslint, unicorn, import, react or jsx-a11y, coverage is high and oxlint alone is enough. If you use plugins outside the fourteen built-in ones, what remains is the alpha jsPlugins mechanism or keeping both tools side by side.

How many rules does oxlint 1.79.0 have?

With every category and every plugin enabled, --print-config prints 870 rules. With no configuration at all, 111 are active, because the correctness category and the unicorn, typescript and oxc plugins run by default.

Do rules requiring type information work?

Yes, after installing oxlint-tsgolint and running with the --type-aware flag. Coverage stands at 59 out of 61 type-aware rules from typescript-eslint. TypeScript 7.0 or newer is required, and baseUrl in tsconfig.json is not supported.

What is the difference between oxlint and oxfmt?

Oxlint inspects code and reports problems, oxfmt rewrites it according to formatting rules. They are two separate npm packages with separate version numbers: 1.79.0 for the linter, 0.64.0 for the formatter. Installing one does not give you the other.

Is oxlint paid?

No. The package, all the binaries and the oxlint-tsgolint component are MIT licensed, with no paid tier and no commercial restrictions. VoidZero sells a separate product called Vite+, towards which the migration documentation points readers looking for an integrated toolchain.

Documentation lives on the Oxc site, the migration guide in the ESLint section, and the source code in the GitHub repository.

Read next

We use cookies to enhance your experience on the site