Skip to content

Migrate to Oxc

Oxfmt and Oxlint are the recommended formatter and linter for new projects. They are written in Rust, run in milliseconds, and the @jmlweb Oxc packages keep the same conventions as the Prettier and ESLint ones.

The Prettier and ESLint packages are in maintenance mode: they keep receiving dependency and security updates, but no new rules. Migrate a project when you are already touching its formatting or lint setup.

Project Formatter Linter
JavaScript @jmlweb/oxfmt-config-base @jmlweb/oxlint-config-base
TypeScript library @jmlweb/oxfmt-config-base @jmlweb/oxlint-config-base
React @jmlweb/oxfmt-config-base @jmlweb/oxlint-config-react
Node.js @jmlweb/oxfmt-config-base @jmlweb/oxlint-config-node
Astro @jmlweb/oxfmt-config-base @jmlweb/oxlint-config-base + @jmlweb/eslint-config-astro for .astro files

Each package page lists what it replaces and what it does not port.

@jmlweb/oxfmt-config-base produces the same output as @jmlweb/prettier-config-base, except that it also sorts imports.

  1. Install the packages and remove Prettier:
Terminal window
pnpm add -D @jmlweb/oxfmt-config-base oxfmt
pnpm remove @jmlweb/prettier-config-base @jmlweb/prettier-config-tailwind prettier prettier-plugin-tailwindcss
  1. Replace .prettierrc* (or the prettier field in package.json) with an oxfmt.config.ts:
import baseConfig from '@jmlweb/oxfmt-config-base';
import { defineConfig } from 'oxfmt';
export default defineConfig({
...baseConfig,
});

For Tailwind CSS, replace @jmlweb/prettier-config-tailwind with Oxfmt’s built-in class sorting:

import baseConfig from '@jmlweb/oxfmt-config-base';
import { defineConfig } from 'oxfmt';
export default defineConfig({
...baseConfig,
sortTailwindcss: {
stylesheet: './src/index.css',
},
});
  1. Move .prettierignore entries to ignorePatterns in the same file, then delete .prettierignore.

  2. Update the scripts and lint-staged:

{
"scripts": {
"format": "oxfmt",
"format:check": "oxfmt --check"
},
"lint-staged": {
"*.{js,mjs,cjs,ts,mts,cts,jsx,tsx,json,md,css}": ["oxfmt"]
}
}
  1. Run pnpm format once and commit the result. Expect import order changes and, rarely, a different line break than Prettier’s. With sortTailwindcss.stylesheet, Oxfmt also moves custom theme classes such as bg-ocean-600 to their correct position: @jmlweb/prettier-config-tailwind never pointed the Tailwind plugin at a stylesheet, so it put them first.

Oxfmt also formats Markdown by default and rewraps hand-written docs. To leave them as they are, add '**/*.md' to ignorePatterns and remove md from the lint-staged pattern.

Oxfmt sorts imports in the same order as eslint-plugin-perfectionist’s sort-imports defaults, so the linter no longer needs a sorting rule. If the project still lints with ESLint, its eslint-plugin-simple-import-sort disagrees with this order: set sortImports: false until you migrate the linter.

Side-effect imports such as import './polyfill' are sorted into their group too. Keep order-dependent ones in a separate entry file.

The Oxlint packages port the ESLint ones rule by rule, including typescript-eslint’s type-aware rules.

  1. Install the package for your project type with its peer dependencies, and remove ESLint:
Terminal window
# TypeScript or JavaScript
pnpm add -D @jmlweb/oxlint-config-base oxlint oxlint-tsgolint
# React
pnpm add -D @jmlweb/oxlint-config-react oxlint oxlint-tsgolint @eslint-react/eslint-plugin eslint-plugin-perfectionist
# Node.js
pnpm add -D @jmlweb/oxlint-config-node oxlint oxlint-tsgolint eslint-plugin-n
pnpm remove eslint @eslint/js typescript-eslint eslint-config-prettier eslint-plugin-simple-import-sort @jmlweb/eslint-config-base

Also remove the @jmlweb/eslint-config-* package and plugins the old config used. eslint-plugin-n stays for Node.js projects, since Oxlint runs it as a plugin.

The ESLint plugins that Oxlint runs, @eslint-react/eslint-plugin, eslint-plugin-perfectionist and eslint-plugin-n, declare eslint as a peer dependency. Your package manager installs it, so eslint stays in the lockfile even though nothing runs it.

  1. Replace eslint.config.* with an oxlint.config.ts:
import reactConfig from '@jmlweb/oxlint-config-react';
import { defineConfig } from 'oxlint';
export default defineConfig({
extends: [reactConfig],
options: {
typeAware: true,
},
});

options.typeAware must be in your own config: Oxlint ignores options in configs loaded through extends, and skips the type-aware rules without telling you.

  1. Update the scripts and lint-staged:
{
"scripts": {
"lint": "oxlint",
"lint:fix": "oxlint --fix"
},
"lint-staged": {
"*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}": ["oxlint --fix"]
}
}
  1. Run pnpm lint and fix the reported problems. The rules are the same as before, so most findings point to code the ESLint setup did not cover, such as files it ignored.
  • @typescript-eslint/naming-convention: It needs typescript-eslint’s parser, which Oxlint cannot run
  • Import sorting rules: Oxfmt sorts imports (see Import Sorting)
  • eslint-config-prettier: Not needed, the Oxlint configs enable no formatting rules
  • React Compiler config and gating checks: They only validate compiler options

Oxlint only lints the <script> blocks of .astro files, and cannot run eslint-plugin-astro. Keep @jmlweb/eslint-config-astro for .astro files and lint everything else with Oxlint:

{
"scripts": {
"lint": "oxlint && eslint \"src/**/*.astro\""
}
}

Oxfmt does not format .astro files either.

Install the Oxc extension for VS Code, which runs both tools, and replace the Prettier and ESLint settings in .vscode/settings.json:

{
"editor.defaultFormatter": "oxc.oxc-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.oxc": "always"
}
}
  • Oxlint reads options.typeAware only from the root config. Run each package’s lint with the root config (oxlint -c ../../oxlint.config.ts src), or give packages their own configs and lint the root with oxlint --disable-nested-config
  • Type-aware rules resolve imports from other workspace packages through their built type declarations, so build dependencies before linting
  • Oxfmt picks the nearest oxfmt.config.ts for each file, so a package can override the root formatting

Set options: { typeAware: true } in your own oxlint.config.ts and install oxlint-tsgolint. Files outside your tsconfig.json, such as vite.config.ts, have no type information: ignore them in ignorePatterns if type-aware rules report errors there.

Oxlint Crashes with Cannot read properties of undefined (reading 'Intrinsic')

Section titled “Oxlint Crashes with Cannot read properties of undefined (reading 'Intrinsic')”

The React config’s JS plugins, @eslint-react/eslint-plugin and eslint-plugin-perfectionist, depend on @typescript-eslint/utils, which supports TypeScript < 6.1. TypeScript 7 has no classic JS API, so the plugins crash. Keep React projects on TypeScript 6.0 until typescript-eslint supports TypeScript 7. tsc and oxlint-tsgolint work with TypeScript 7.

Oxlint Is Slow or Reports Problems in Dependencies

Section titled “Oxlint Is Slow or Reports Problems in Dependencies”

Without a .gitignore that lists node_modules, Oxlint lints your dependencies. Add it to .gitignore, or to ignorePatterns.

n/no-unpublished-import Reports Tests or Config Files

Section titled “n/no-unpublished-import Reports Tests or Config Files”

eslint-plugin-n treats files as published unless the package is private or the files are outside the files field of package.json. Mark applications as "private": true.

n/hashbang or n/no-missing-import Report a CLI Built to dist/

Section titled “n/hashbang or n/no-missing-import Report a CLI Built to dist/”

When bin points at build output, n/hashbang reports This file needs no shebang. on the source file, and n/no-missing-import fails on scripts that import dist/. Map source to build paths with the n.convertPath setting and turn n/no-missing-import off for those scripts. See the @jmlweb/oxlint-config-node README for the config.

Oxlint overrides entries accept files, rules, plugins, jsPlugins, env and globals, but not extends. To apply another shared config to part of the project, such as @jmlweb/oxlint-config-node for a server/ folder in a React app, put an oxlint.config.ts that extends it in that folder. Oxlint uses the nearest config for each file.

Since Oxlint 1.66, oxlint --rules prints nothing when run by an AI coding agent: Oxlint detects the agent from environment variables such as CLAUDECODE and switches to its agent output format, which cannot list rules. Pass the format explicitly, oxlint --rules --format default or --format json. To see the rules your config enables, run oxlint --print-config.