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.
Which Packages to Use
Section titled “Which Packages to Use”| 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.
Formatter: Prettier to Oxfmt
Section titled “Formatter: Prettier to Oxfmt”@jmlweb/oxfmt-config-base produces the same output as @jmlweb/prettier-config-base, except that it also sorts imports.
- Install the packages and remove Prettier:
pnpm add -D @jmlweb/oxfmt-config-base oxfmtpnpm remove @jmlweb/prettier-config-base @jmlweb/prettier-config-tailwind prettier prettier-plugin-tailwindcss- Replace
.prettierrc*(or theprettierfield inpackage.json) with anoxfmt.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', },});-
Move
.prettierignoreentries toignorePatternsin the same file, then delete.prettierignore. -
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"] }}- Run
pnpm formatonce and commit the result. Expect import order changes and, rarely, a different line break than Prettier’s. WithsortTailwindcss.stylesheet, Oxfmt also moves custom theme classes such asbg-ocean-600to their correct position:@jmlweb/prettier-config-tailwindnever 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.
Import Sorting
Section titled “Import Sorting”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.
Linter: ESLint to Oxlint
Section titled “Linter: ESLint to Oxlint”The Oxlint packages port the ESLint ones rule by rule, including typescript-eslint’s type-aware rules.
- Install the package for your project type with its peer dependencies, and remove ESLint:
# TypeScript or JavaScriptpnpm add -D @jmlweb/oxlint-config-base oxlint oxlint-tsgolint
# Reactpnpm add -D @jmlweb/oxlint-config-react oxlint oxlint-tsgolint @eslint-react/eslint-plugin eslint-plugin-perfectionist
# Node.jspnpm 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-baseAlso 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.
- Replace
eslint.config.*with anoxlint.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.
- 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"] }}- Run
pnpm lintand 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.
What Is Not Ported
Section titled “What Is Not Ported”@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
configandgatingchecks: They only validate compiler options
Astro Projects
Section titled “Astro Projects”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.
Editor Setup
Section titled “Editor Setup”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" }}Monorepos
Section titled “Monorepos”- Oxlint reads
options.typeAwareonly 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 withoxlint --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.tsfor each file, so a package can override the root formatting
Common Issues
Section titled “Common Issues”Type-Aware Rules Do Not Report
Section titled “Type-Aware Rules Do Not Report”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.
An Override Needs Another Shared Config
Section titled “An Override Needs Another Shared 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.
Checking Which Rules Apply
Section titled “Checking Which Rules Apply”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.