deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

Oxlint on Excalidraw: lint time fell from 60s to 2s, and 435 new warnings needed review

A dev.to trial ran Oxlint and Oxfmt against Excalidraw's React codebase: a 60-second lint-and-format check fell to about two seconds, but Oxlint also surfaced 435 warnings ESLint had never reported.

Oxlint on Excalidraw: lint time fell from 60s to 2s, and 435 new warnings needed review

Rust tooling meets a five-year-old codebase

A hands-on trial published on dev.to pointed Oxlint and Oxfmt, the Rust-based linter and formatter from the Oxc project, at a real codebase instead of a synthetic benchmark. The target, Excalidraw, saw its combined lint-and-format check fall from about a minute to roughly two seconds. The more revealing result was what the faster linter reported once it finished. According to the write-up, the two tools are downloaded about 26 million times a week, against documentation claims that Oxlint is 50 to 100 times faster than ESLint and Oxfmt roughly 30 times faster than Prettier.

The setup

Excalidraw, the open-source whiteboard app, is a React and TypeScript codebase of 710 JS/TS files, configured the way many projects still are: ESLint 7 with Create React App's rule set, and Prettier 2.6 running inside ESLint as a plugin. Timings came from an ordinary laptop (Ryzen 5 5600H, 12 threads, 15 GB RAM), using one warm-up run and three timed runs per command and reporting the median.

What the stopwatch said

  • ESLint with the Prettier plugin, as Excalidraw ships it: 60.2 seconds
  • ESLint with the Prettier rule switched off: 50.7 seconds
  • Prettier 2.6 on its own: 15.3 seconds
  • Oxlint with Excalidraw's rules ported over: 1.5 seconds
  • Oxfmt: 0.41 seconds

That puts the full check at roughly 30x faster — real, but short of the 50–100x headline. The gap is explained by plugins: zero-config Oxlint finished in 0.46 seconds, and enabling React, import and accessibility rules raised that to 1.3–1.5 seconds.

The trial also surfaced a finding unrelated to Oxlint: running Prettier as a plugin inside ESLint cost about 10 seconds per run. Teams doing the same can separate the two tools today, without adopting anything new.

The 435 warnings

ESLint reported zero warnings on the codebase. Oxlint, with the same rules ported over, reported 435 warnings and 1 error — and most of them were not bugs. The triage found 145 unused variables and parameters, where Oxlint's defaults are simply stricter; 48 of 50 warnings about numeric precision loss came from a single file of deliberately long mathematical constants; and the lone error was a target="_blank" link that already carried rel="noopener" but was assembled from a template string, on a line Excalidraw had told ESLint to skip.

Some findings were genuinely useful. Oxlint's React rules flagged 22 places that call setState directly inside an effect — newer React guidance prefers calculating the value during render — plus 10 async Promise executors. ESLint 7's rule set simply doesn't know about those patterns. The write-up's conclusion: the speed gain is immediate, but the engineering effort shifts to deciding, rule by rule, which of the new warnings actually matter.

What didn't migrate

Three of Excalidraw's rules have no built-in Oxlint equivalent. The most consequential is no-restricted-syntax, which the project uses to block plain text in JSX in favour of t(...) calls and keep the app translatable — a genuine guard that would be lost. The others are import/order (Oxfmt can sort imports, but that is a formatting change, not a lint error) and dot-notation, which the write-up describes as minor.

Oxlint's answer for missing rules is JS plugins, which load original ESLint plugins inside Oxlint, but that feature is still marked alpha. The supported alternative is running both linters, with eslint-plugin-oxlint disabling the rules Oxlint already covers. There is also a migration trap: @oxlint/migrate only reads ESLint v9/v10 flat configs, so Excalidraw's legacy .eslintrc.json had to be ported by hand. Projects on ESLint 8 or older should expect the same.

The formatter migrates cleanly

Oxfmt's migration command, npx oxfmt --migrate=prettier, worked on the first attempt and handled a default mismatch, keeping Excalidraw at Prettier's 80-character line width instead of Oxfmt's 100. It wanted to reformat 102 files (+1,163/−1,092 lines), which sounds alarming until compared with Prettier 3, which wants to change 126 files in the same repository. Every file on Oxfmt's list was also on Prettier 3's, so the churn comes from leaving Prettier 2's old style rather than from Oxfmt being unusual. Separately, Prettier 2.6 cannot parse one of Excalidraw's TypeScript files that uses newer generic syntax — the team had added a skip comment — while Oxfmt read it without complaint.

Why it matters

The trial ends with staged, low-risk advice: pull Prettier out of ESLint for an immediate saving; trial Oxfmt in a branch, committing the reformat separately and adding that commit to .git-blame-ignore-revs so git blame stays useful; and run Oxlint alongside ESLint, retiring the older tool only when nothing the team relies on is left behind. Adoption is already underway — Bluesky's app runs Oxlint as its lint script, and the Oxc documentation lists Sentry, Kibana, Electron and PostHog as users. The wider lesson for teams weighing Rust-based tooling: benchmarks sell the speed, and the speed is real, but migration cost lives in rule parity and warning triage rather than in waiting time.

  • #linting
  • #rust
  • #javascript
  • #react
  • #developer-tools

Related posts