---
title: "Oxlint 1.0: a 50x faster linter, and how it fits next to ESLint · vuejs.school"
description: "Oxlint 1.0 lints a Vue project in under a second, but it only sees script blocks. Here is what it covers, what it misses, and how to run it before ESLint."
canonical_url: "https://vuejs.school/community/blog/oxlint-1-0-a-faster-linter-next-to-eslint"
meta:
  "og:description": "Oxlint 1.0 lints a Vue project in under a second, but it only sees script blocks. Here is what it covers, what it misses, and how to run it before ESLint."
  "og:image": "https://vuejs.school/_og/d/a_~T3hsaW50IDEuMDogYSA1MHggZmFzdGVyIGxpbnRlciwgYW5kIGhvdyBpdCBmaXRzIG5leHQgdG8gRVNMaW50IMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~T3hsaW50IDEuMDogYSA1MHggZmFzdGVyIGxpbnRlciwgYW5kIGhvdyBpdCBmaXRzIG5leHQgdG8gRVNMaW50%2Cdescription_~T3hsaW50IDEuMCBsaW50cyBhIFZ1ZSBwcm9qZWN0IGluIHVuZGVyIGEgc2Vjb25kLCBidXQgaXQgb25seSBzZWVzIHNjcmlwdCBibG9ja3MuIEhlcmUgaXMgd2hhdCBpdCBjb3ZlcnMsIHdoYXQgaXQgbWlzc2VzLCBhbmQgaG93IHRvIHJ1biBpdCBiZWZvcmUgRVNMaW50Lg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy9veGxpbnQtMS0wLWEtZmFzdGVyLWxpbnRlci1uZXh0LXRvLWVzbGludCI%2Cs_JBJqZ3lqZrxDn8Zj.png"
  "og:image:alt": "Oxlint 1.0: a 50x faster linter, and how it fits next to ESLint · vuejs.school"
  "og:image:height": "600"
  "og:image:type": image/png
  "og:image:width": "1200"
  "og:title": "Oxlint 1.0: a 50x faster linter, and how it fits next to ESLint · vuejs.school"
  "twitter:image": "https://vuejs.school/_og/d/a_~T3hsaW50IDEuMDogYSA1MHggZmFzdGVyIGxpbnRlciwgYW5kIGhvdyBpdCBmaXRzIG5leHQgdG8gRVNMaW50IMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~T3hsaW50IDEuMDogYSA1MHggZmFzdGVyIGxpbnRlciwgYW5kIGhvdyBpdCBmaXRzIG5leHQgdG8gRVNMaW50%2Cdescription_~T3hsaW50IDEuMCBsaW50cyBhIFZ1ZSBwcm9qZWN0IGluIHVuZGVyIGEgc2Vjb25kLCBidXQgaXQgb25seSBzZWVzIHNjcmlwdCBibG9ja3MuIEhlcmUgaXMgd2hhdCBpdCBjb3ZlcnMsIHdoYXQgaXQgbWlzc2VzLCBhbmQgaG93IHRvIHJ1biBpdCBiZWZvcmUgRVNMaW50Lg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy9veGxpbnQtMS0wLWEtZmFzdGVyLWxpbnRlci1uZXh0LXRvLWVzbGludCI%2Cs_JBJqZ3lqZrxDn8Zj.png"
  "twitter:image:alt": "Oxlint 1.0: a 50x faster linter, and how it fits next to ESLint · vuejs.school"
---

> Index of every page on this site: [llms.txt](https://vuejs.school/llms.txt)

## Rules, speed and the 1.0 versioning promise

The [announcement](https://oxc.rs/blog/2025-06-10-oxlint-stable) describes over 500 rules: the complete ESLint core rule set, the `typescript-eslint` rules that do not need type information, and rules ported from `eslint-plugin-unicorn`, `jsdoc`, `react`, `react-hooks`, `jest` and `import`, plus a few of Oxlint's own, such as `const-comparisons` for comparisons that can never be true.

The speed claim is 50 to 100 times faster than ESLint with the same rules. The announcement's benchmark has Oxlint finishing in 615 milliseconds where ESLint takes 33.5 seconds, and it quotes a throughput of roughly 10,000 files per second, depending on how many threads the machine has.

Production users are named too: Shopify's admin console, Airbnb running multi-file rules such as `import/no-cycle` across 126,000 files in 7 seconds on CI, and Mercedes-Benz reporting a 71 percent drop in lint time after switching.

Stable means a versioning promise. From 1.0, Oxlint follows semantic versioning: a patch release only fixes bugs, a minor release adds rules and diagnostics without requiring configuration changes, and a major release is the only kind that may change the command line or the config file format. The announcement adds one caveat: a minor release can still turn your CI red, because a new rule can find a problem that was always there. Pin the version in CI and bump it on purpose.

## What Oxlint 1.0 cannot check

The Oxlint docs state three gaps that matter for a Vue project.

It has no type-aware rules. Rules such as `no-floating-promises` or `no-misused-promises` need the TypeScript type checker, and Oxlint does not run one. If your ESLint config uses `typescript-eslint`'s type-checked presets, those rules stay in ESLint.

It cannot run custom rules. The docs say custom plugins are not supported "at this time", and the announcement says JavaScript plugin support is "coming soon". Until then, a rule your team wrote lives in ESLint.

It lints only the `<script>` block of a `.vue` file. Everything `eslint-plugin-vue` checks in a template is outside Oxlint's view: `vue/valid-v-for`, `vue/require-v-for-key`, `vue/no-unused-vars` for variables declared in the template, and the rest of the `vue/` rule set. The docs' plugins page says the team has no plans to support rules from plugins that need their own parser, such as `eslint-plugin-vue`, but is open to those that only affect the script.

Stylistic rules, such as indentation and quote style, are not supported either. Leave those to a formatter.

## Run it on a Vue project without a config

No configuration is needed for a first look. From the project root:

```
npx oxlint@latest src
```

By default Oxlint turns on one category of rules, `correctness`, which the docs define as code that is definitely wrong or useless: `no-unused-vars`, `no-debugger`, `no-constant-condition`, `no-self-assign` and the rest of that category. These come from ESLint's core rules and the three plugins Oxlint enables by default: `typescript`, `unicorn` and `oxc`.

Oxlint reads the `lang` attribute of each script block, so it parses `<script setup lang="ts">` as TypeScript without being told. A project the size of a fresh create-vue app is done in a fraction of a second.

The command line can also set rules and categories:

- `-D` denies a rule or category (reports it as an error), `-W` warns and `-A` allows (turns it off). They apply from left to right.
- `--fix` applies the safe automatic fixes.
- `--deny-warnings` makes warnings fail the run, which is what you want in CI.

This run makes `correctness` an error, warns on `suspicious`, allows `no-console` and fixes what it can:

```
npx oxlint@latest src -D correctness -W suspicious -A no-console --fix
```

## Configure it with .oxlintrc.json

For anything beyond a one-off run, put the settings in `.oxlintrc.json` at the project root. Oxlint finds it automatically, and the format follows ESLint's older configuration style (`.eslintrc`), so the keys look familiar. The one surprise is `plugins`: setting it replaces the default list instead of adding to it. Name the three defaults (`typescript`, `unicorn`, `oxc`) as well as the plugins you add, here `import` and `vitest`:

*.oxlintrc.json*

```
{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "plugins": ["typescript", "unicorn", "oxc", "import", "vitest"],
  "categories": {
    "correctness": "error",
    "suspicious": "warn"
  },
  "env": { "browser": true },
  "ignorePatterns": ["dist/**", "coverage/**"],
  "rules": {
    "no-console": "warn"
  },
  "overrides": [
    {
      "files": ["src/**/__tests__/*"],
      "rules": { "no-console": "off" }
    }
  ]
}
```

- `$schema` lets your editor complete and check the keys.
- `categories` switches whole groups on with a severity. `suspicious` is code that is probably wrong; it is a good second category once `correctness` is clean.
- `rules` sets individual rules by name, with the plugin prefix when a name is ambiguous.
- `overrides` applies different settings to matching files, here allowing `console` in tests.

If you already have an `eslint.config.js`, the `oxlint-migrate` tool (`npx @oxlint/migrate`) converts it into an `.oxlintrc.json` as a starting point.

## Run Oxlint first and ESLint second

The Oxlint docs say it can fully replace ESLint in small and medium projects. For larger ones, they advise running Oxlint first for fast feedback, then ESLint with the duplicated rules switched off, so that every problem is reported once. A Vue project needs that second setup whatever its size, because only ESLint reads templates. `eslint-plugin-oxlint` does the switching off: its configs turn off the ESLint rules that Oxlint already runs.

Install the three packages (`npm-run-all2` provides the `run-s` command used below):

```
npm install -D oxlint eslint-plugin-oxlint npm-run-all2
```

Then add the plugin's config as the last entry of your flat config (the `eslint.config.*` file ESLint 9 uses), after `eslint-plugin-vue` and the TypeScript config, so it can turn off rules the earlier entries turned on. This is a create-vue style `eslint.config.ts`:

*eslint.config.ts*

```
import { globalIgnores } from 'eslint/config'
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
import pluginVue from 'eslint-plugin-vue'
import pluginOxlint from 'eslint-plugin-oxlint'

export default defineConfigWithVueTs(
  { name: 'app/files-to-lint', files: ['**/*.{ts,mts,tsx,vue}'] },
  globalIgnores(['**/dist/**', '**/coverage/**']),
  pluginVue.configs['flat/essential'],
  vueTsConfigs.recommended,
  ...pluginOxlint.configs['flat/recommended'],
)
```

`flat/recommended` turns off the ESLint versions of the rules in Oxlint's `correctness` category. It knows Oxlint's blind spot: it leaves `no-unused-vars` and `@typescript-eslint/no-unused-vars` alone in `.vue` files, so ESLint keeps checking them there. The config is an array, hence the spread (`...`). If you enabled more in `.oxlintrc.json`, use `...pluginOxlint.buildFromOxlintConfigFile('./.oxlintrc.json')` instead, and the plugin reads your file and turns off exactly what you turned on.

Finally, chain the two in `package.json` so `npm run lint` runs Oxlint and only continues to ESLint when it passes:

*package.json*

```
{
  "scripts": {
    "lint:oxlint": "oxlint . --fix -D correctness --ignore-path .gitignore",
    "lint:eslint": "eslint . --fix",
    "lint": "run-s lint:*"
  }
}
```

This is the same layout the create-vue scaffolder generates when you answer yes to its Oxlint question (marked experimental), so a new project can start here without typing any of it. Its version ranges lag behind, though: create-vue 3.16 writes `oxlint` and `eslint-plugin-oxlint` as `^0.16.0`, a range that never reaches 1.0, so raise both to `^1.0.0`.

In practice the Oxlint step finishes almost at once, and ESLint then spends its time on the rest, above all the template rules and unused variables in components.

## Add it to your editor and CI

In the editor you want both. The official `oxc.oxc-vscode` extension shows Oxlint diagnostics as you type; keep the ESLint extension installed for the `vue/` rules. The announcement also lists extensions for IntelliJ IDEA, WebStorm and Zed, and a language server for other editors.

In CI, pin the version and fail on warnings. These are the steps of the lint job in a GitHub Actions workflow:

*.github/workflows/lint.yml*

```
steps:
  - uses: actions/checkout@v4
  - run: npx --yes oxlint@1.0.0 --deny-warnings
```

For pre-commit hooks, the docs give a `lint-staged` entry. Narrowed to a Vue project's files, it is `"**/*.{js,ts,vue}": "oxlint"`, which lints only the staged files and is fast enough to run on every commit.

## Add it today or wait

Add it today as the first pass. The install is three packages, the config above is small, and the feedback in the editor is immediate. Do not remove ESLint from a Vue project: the template rules, unused variables in components, type-aware rules and any custom rule still need it. The announcement itself recommends running `oxlint && eslint`.

Wait only if your project has no TypeScript and a tiny ESLint config that already runs in a couple of seconds. Then the second tool is overhead without a visible gain, and you can revisit when the planned JavaScript plugin support lands.

**Check it stuck**3 questions from the Linting and formatting quiz.

[**Practice now**](https://vuejs.school/learn/tooling/linting-and-formatting?tab=quiz)

**Was this helpful?**

## **Keep reading**

[Tutorial · 8 min read**Editor and Vue DevTools setup in 2026**](https://vuejs.school/community/blog/editor-and-vue-devtools-setup-in-2026) [Release notes · 6 min read**Rolldown 1.0: the bundler under Vite 8 is stable**](https://vuejs.school/community/blog/rolldown-1-0-the-bundler-under-vite-8-is-stable)

```json
{"@context":"https://schema.org","@graph":[{"@id":"https://vuejs.school/#website","@type":"WebSite","description":"A free AI-instructed school for Vue.js. Short tutorials, runnable code and quizzes that adapt to you.","name":"vuejs.school","url":"https://vuejs.school/","publisher":{"@id":"https://vuejs.school/#identity"}},{"@id":"https://vuejs.school/community/blog/oxlint-1-0-a-faster-linter-next-to-eslint#webpage","@type":"WebPage","dateModified":"2026-10-27T19:31:40.253Z","datePublished":"2025-06-12","description":"Oxlint 1.0 lints a Vue project in under a second, but it only sees script blocks. Here is what it covers, what it misses, and how to run it before ESLint.","name":"Oxlint 1.0: a 50x faster linter, and how it fits next to ESLint","url":"https://vuejs.school/community/blog/oxlint-1-0-a-faster-linter-next-to-eslint","breadcrumb":{"@id":"https://vuejs.school/community/blog/oxlint-1-0-a-faster-linter-next-to-eslint#breadcrumb"},"isPartOf":{"@id":"https://vuejs.school/#website"},"potentialAction":[{"@type":"ReadAction","target":["https://vuejs.school/community/blog/oxlint-1-0-a-faster-linter-next-to-eslint"]}],"primaryImageOfPage":{"@id":"https://vuejs.school/#logo"}},{"@id":"https://vuejs.school/#identity","@type":"Organization","description":"A free AI-instructed school for Vue.js. Short tutorials, runnable code and quizzes that adapt to you.","name":"vuejs.school","url":"https://vuejs.school","logo":{"@id":"https://vuejs.school/#logo"}},{"@id":"https://vuejs.school/community/blog/oxlint-1-0-a-faster-linter-next-to-eslint#article","dateModified":"2026-10-27T19:31:40.253Z","datePublished":"2025-06-12","description":"Oxlint 1.0 lints a Vue project in under a second, but it only sees script blocks. Here is what it covers, what it misses, and how to run it before ESLint.","headline":"Oxlint 1.0: a 50x faster linter, and how it fits next to ESLint","inLanguage":"en","isAccessibleForFree":true,"thumbnailUrl":"https://vuejs.school/_og/d/a_~T3hsaW50IDEuMDogYSA1MHggZmFzdGVyIGxpbnRlciwgYW5kIGhvdyBpdCBmaXRzIG5leHQgdG8gRVNMaW50IMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~T3hsaW50IDEuMDogYSA1MHggZmFzdGVyIGxpbnRlciwgYW5kIGhvdyBpdCBmaXRzIG5leHQgdG8gRVNMaW50%2Cdescription_~T3hsaW50IDEuMCBsaW50cyBhIFZ1ZSBwcm9qZWN0IGluIHVuZGVyIGEgc2Vjb25kLCBidXQgaXQgb25seSBzZWVzIHNjcmlwdCBibG9ja3MuIEhlcmUgaXMgd2hhdCBpdCBjb3ZlcnMsIHdoYXQgaXQgbWlzc2VzLCBhbmQgaG93IHRvIHJ1biBpdCBiZWZvcmUgRVNMaW50Lg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy9veGxpbnQtMS0wLWEtZmFzdGVyLWxpbnRlci1uZXh0LXRvLWVzbGludCI%2Cs_JBJqZ3lqZrxDn8Zj.png","@type":["Article","BlogPosting"],"articleSection":["Release notes"],"author":{"@id":"https://vuejs.school/#identity"},"image":{"@id":"https://vuejs.school/#/schema/image/1"},"isPartOf":{"@id":"https://vuejs.school/community/blog/oxlint-1-0-a-faster-linter-next-to-eslint#webpage"},"mainEntityOfPage":{"@id":"https://vuejs.school/community/blog/oxlint-1-0-a-faster-linter-next-to-eslint#webpage"},"publisher":{"@id":"https://vuejs.school/#identity"}},{"@id":"https://vuejs.school/community/blog/oxlint-1-0-a-faster-linter-next-to-eslint#breadcrumb","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","name":"Blog","item":"https://vuejs.school/community/blog","position":1},{"@type":"ListItem","name":"Oxlint 1.0: a 50x faster linter, and how it fits next to ESLint","position":2}]},{"@id":"https://vuejs.school/#logo","@type":"ImageObject","caption":"vuejs.school","contentUrl":"https://vuejs.school/apple-touch-icon.png","inLanguage":"en","url":"https://vuejs.school/apple-touch-icon.png"},{"@id":"https://vuejs.school/#/schema/image/1","@type":"ImageObject","contentUrl":"https://vuejs.school/_og/d/a_~T3hsaW50IDEuMDogYSA1MHggZmFzdGVyIGxpbnRlciwgYW5kIGhvdyBpdCBmaXRzIG5leHQgdG8gRVNMaW50IMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~T3hsaW50IDEuMDogYSA1MHggZmFzdGVyIGxpbnRlciwgYW5kIGhvdyBpdCBmaXRzIG5leHQgdG8gRVNMaW50%2Cdescription_~T3hsaW50IDEuMCBsaW50cyBhIFZ1ZSBwcm9qZWN0IGluIHVuZGVyIGEgc2Vjb25kLCBidXQgaXQgb25seSBzZWVzIHNjcmlwdCBibG9ja3MuIEhlcmUgaXMgd2hhdCBpdCBjb3ZlcnMsIHdoYXQgaXQgbWlzc2VzLCBhbmQgaG93IHRvIHJ1biBpdCBiZWZvcmUgRVNMaW50Lg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy9veGxpbnQtMS0wLWEtZmFzdGVyLWxpbnRlci1uZXh0LXRvLWVzbGludCI%2Cs_JBJqZ3lqZrxDn8Zj.png","inLanguage":"en","url":"https://vuejs.school/_og/d/a_~T3hsaW50IDEuMDogYSA1MHggZmFzdGVyIGxpbnRlciwgYW5kIGhvdyBpdCBmaXRzIG5leHQgdG8gRVNMaW50IMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~T3hsaW50IDEuMDogYSA1MHggZmFzdGVyIGxpbnRlciwgYW5kIGhvdyBpdCBmaXRzIG5leHQgdG8gRVNMaW50%2Cdescription_~T3hsaW50IDEuMCBsaW50cyBhIFZ1ZSBwcm9qZWN0IGluIHVuZGVyIGEgc2Vjb25kLCBidXQgaXQgb25seSBzZWVzIHNjcmlwdCBibG9ja3MuIEhlcmUgaXMgd2hhdCBpdCBjb3ZlcnMsIHdoYXQgaXQgbWlzc2VzLCBhbmQgaG93IHRvIHJ1biBpdCBiZWZvcmUgRVNMaW50Lg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy9veGxpbnQtMS0wLWEtZmFzdGVyLWxpbnRlci1uZXh0LXRvLWVzbGludCI%2Cs_JBJqZ3lqZrxDn8Zj.png"}]}
```