---
title: "ESLint 10 with eslint-plugin-vue: the upgrade · vuejs.school"
description: "ESLint 10 removes eslintrc and old Node versions. Vue's lint plugins do not list it as supported yet, so here is what to fix now and when to upgrade."
canonical_url: "https://vuejs.school/community/blog/eslint-10-with-eslint-plugin-vue"
meta:
  "og:description": "ESLint 10 removes eslintrc and old Node versions. Vue's lint plugins do not list it as supported yet, so here is what to fix now and when to upgrade."
  "og:image": "https://vuejs.school/_og/d/a_~RVNMaW50IDEwIHdpdGggZXNsaW50LXBsdWdpbi12dWU6IHRoZSB1cGdyYWRlIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~RVNMaW50IDEwIHdpdGggZXNsaW50LXBsdWdpbi12dWU6IHRoZSB1cGdyYWRl%2Cdescription_~RVNMaW50IDEwIHJlbW92ZXMgZXNsaW50cmMgYW5kIG9sZCBOb2RlIHZlcnNpb25zLiBWdWUncyBsaW50IHBsdWdpbnMgZG8gbm90IGxpc3QgaXQgYXMgc3VwcG9ydGVkIHlldCwgc28gaGVyZSBpcyB3aGF0IHRvIGZpeCBub3cgYW5kIHdoZW4gdG8gdXBncmFkZS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy9lc2xpbnQtMTAtd2l0aC1lc2xpbnQtcGx1Z2luLXZ1ZSI%2Cs_nwynLV6-vkcp1tha.png"
  "og:image:alt": "ESLint 10 with eslint-plugin-vue: the upgrade · vuejs.school"
  "og:image:height": "600"
  "og:image:type": image/png
  "og:image:width": "1200"
  "og:title": "ESLint 10 with eslint-plugin-vue: the upgrade · vuejs.school"
  "twitter:image": "https://vuejs.school/_og/d/a_~RVNMaW50IDEwIHdpdGggZXNsaW50LXBsdWdpbi12dWU6IHRoZSB1cGdyYWRlIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~RVNMaW50IDEwIHdpdGggZXNsaW50LXBsdWdpbi12dWU6IHRoZSB1cGdyYWRl%2Cdescription_~RVNMaW50IDEwIHJlbW92ZXMgZXNsaW50cmMgYW5kIG9sZCBOb2RlIHZlcnNpb25zLiBWdWUncyBsaW50IHBsdWdpbnMgZG8gbm90IGxpc3QgaXQgYXMgc3VwcG9ydGVkIHlldCwgc28gaGVyZSBpcyB3aGF0IHRvIGZpeCBub3cgYW5kIHdoZW4gdG8gdXBncmFkZS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy9lc2xpbnQtMTAtd2l0aC1lc2xpbnQtcGx1Z2luLXZ1ZSI%2Cs_nwynLV6-vkcp1tha.png"
  "twitter:image:alt": "ESLint 10 with eslint-plugin-vue: the upgrade · vuejs.school"
---

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

## What ESLint 10 changes

The main breaking changes for users, from the migration guide:

- **Node.js.** ESLint now needs Node `^20.19.0`, `^22.13.0` or 24 and newer. Node 18, 21 and 23 are dropped. The guide reminds you to check the Node version your editor uses for its ESLint integration too.
- **eslintrc is removed.** `.eslintrc.*` and `.eslintignore` files are no longer read, the `ESLINT_USE_FLAT_CONFIG` variable is ignored, and the CLI flags that only made sense for the old system are gone: `--no-eslintrc`, `--env`, `--resolve-plugins-relative-to`, `--rulesdir` and `--ignore-path`.
- **`/* eslint-env */` comments are errors.** They used to declare globals for one file. Now ESLint reports them.
- **Config lookup starts from each file.** ESLint looks for `eslint.config.*` from the directory of every file it lints, walking up, rather than from the folder you ran it in. In a monorepo, each package can have its own config in the same run.
- **`eslint:recommended` gained three rules:** `no-unassigned-vars`, `no-useless-assignment` and `preserve-caught-error`.
- **JSX references are tracked**, so `no-unused-vars` and `no-undef` understand `<Card />` in `.tsx` files.
- **TypeScript config files** need jiti 2.2.0 or newer, the library ESLint uses to load an `eslint.config.ts`.

Smaller changes: `no-shadow-restricted-names` now reports a variable called `globalThis`, the `"always"` and `"as-needed"` options of `radix` are deprecated, the default `stylish` formatter respects `NO_COLOR`, and the `func-names` and `no-invalid-regexp` options are validated more strictly.

Plugin authors get a longer list. The old rule context methods such as `context.getSourceCode()` and `context.getFilename()` are removed, and so are several `SourceCode` methods. Any plugin still calling one of them breaks on ESLint 10, which is why plugins publish their own release before declaring support.

## The Vue plugins do not list ESLint 10 yet

A package says which ESLint versions it supports in its peer dependency range. On 12 February the current releases read:

- `eslint-plugin-vue` 10.7.0, from 16 January: ESLint `^8.57.0 || ^9.0.0`.
- `vue-eslint-parser` 10.2.0, the parser that reads `.vue` files for it: the same range.
- `typescript-eslint` 8.55.0, from 9 February: the same range.
- `@vue/eslint-config-typescript` 14.6.0, which ties the Vue plugin and typescript-eslint together: ESLint `^9.10.0`.

None of them includes `^10.0.0` yet. Run `npm install -D eslint@10` in such a project and npm stops with an `ERESOLVE` error that names the conflicting peer range; pnpm installs and prints peer warnings.

Do not force it with `--legacy-peer-deps`. A peer range is the plugin authors telling you what they have tested, and the removed context methods above are exactly the kind of change that breaks a plugin at runtime in ways a warning will not show.

You can check the current state at any time:

```
npm view eslint-plugin-vue peerDependencies
npm view vue-eslint-parser peerDependencies
npm view typescript-eslint peerDependencies
npm view @vue/eslint-config-typescript peerDependencies
```

When all four print an `eslint` range that includes `^10.0.0`, you are clear to upgrade.

## Your config file does not change

This is the shape create-vue 3.21 writes for a TypeScript project, with the `recommended` preset in place of `essential` and two rules of our own. It runs on ESLint 9.39 with eslint-plugin-vue 10.7 today, and it is already in the only format ESLint 10 reads.

*eslint.config.ts*

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

export default defineConfigWithVueTs(
  {
    name: 'app/files-to-lint',
    files: ['**/*.{vue,ts,mts,tsx}'],
  },

  globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),

  ...pluginVue.configs['flat/recommended'],
  vueTsConfigs.recommended,

  {
    rules: {
      'vue/block-order': ['error', { order: ['script', 'template', 'style'] }],
      'vue/no-import-compiler-macros': 'error',
    },
  },
)
```

`defineConfigWithVueTs()` and `vueTsConfigs` come from `@vue/eslint-config-typescript` and apply typescript-eslint's recommended rules to `.vue` files as well as `.ts` files. `flat/recommended` adds eslint-plugin-vue's readability and consistency rules to the error checks of `essential`. It already sets `vue/block-order` as a warning that accepts `<script>` and `<template>` in either order; the rules block makes it an error with `<script>` first. `vue/no-import-compiler-macros` reports an import such as `import { defineProps } from 'vue'`, because compiler macros do not need one.

The matching `devDependencies` in create-vue 3.21 are `eslint` ^9.39.2, `eslint-plugin-vue` \~10.7.0, `@vue/eslint-config-typescript` ^14.6.0, `typescript` \~5.9.3 and `jiti` ^2.6.1. The config file ends in `.ts` because create-vue renames every generated `.js` file in a TypeScript project, and jiti is what lets ESLint load it. jiti 2.6 already meets ESLint 10's new minimum of 2.2.0.

## What changed since the March 2025 setup

If your config dates from the eslint-plugin-vue 10.0 release last March, four things are new.

`globalIgnores()` from `eslint/config` replaces a bare `{ ignores: [...] }` object. ESLint 9.22 added the `eslint/config` entry with `defineConfig()` and `globalIgnores()`. A config object with only `ignores` already acted globally, but the helper says so in its name, so nobody later adds a `files` key and quietly turns it into a local ignore.

create-vue gives each config object a `name`. Names show up when you run `npx eslint --inspect-config`, which opens a view of every object and the rules it sets, and they help once a config has more than three parts.

The plugin's minors added rules, none of them switched on by the presets: `vue/no-negated-v-if-condition` in 10.4, `vue/no-duplicate-class-names` in 10.6, and `vue/no-undef-directives` and `vue/no-literals-in-template` in 10.7. The first one that pays off in most apps is `vue/no-undef-directives`, which reports a custom directive used in a template but never defined. Add it to your own rules block.

Version 10.7 also added `-error` variants of the presets, such as `flat/recommended-error`, which set every rule to `error` instead of mixing in warnings. Use it if your CI fails on errors only and warnings tend to pile up unread.

## Get ready now, upgrade later

Everything ESLint 10 removes can be cleaned up while you are still on 9.39, and none of it breaks anything there. Start with your `lint` script. Old flags such as `--ext` and `--ignore-path` can sit in `package.json` for years:

*package.json**Breaks***

```
{
  "scripts": {
    "lint": "eslint . --ext .vue,.ts --ignore-path .gitignore"
  }
}
```

*package.json**Works***

```
{
  "scripts": {
    "lint": "eslint . --fix --cache"
  }
}
```

`--ignore-path` belongs to the old system. ESLint 9 accepts it only in eslintrc mode: with an `eslint.config.*` file it already stops with an `Invalid option '--ignore-path'` error. If your script still runs with it, the project is still on eslintrc, which ESLint 10 removes. Move to flat config with the [configuration migration guide](https://eslint.org/docs/latest/use/configure/migration-guide) and put the ignore patterns in `globalIgnores()`.

`--ext` still exists in ESLint 10, but it is redundant, because the `files` pattern in the config already says which files to lint.

Then work through the rest:

1. Check `node --version` locally, in CI and in your editor. Anything outside ESLint 10's range ( `^20.19.0`, `^22.13.0` or 24 and newer) has to move first.
2. Delete any leftover `.eslintrc.*` or `.eslintignore` file. ESLint 9 with flat config already ignored them, so removing them changes nothing except the confusion.
3. Search for `eslint-env` comments and replace each with a `languageOptions.globals` entry, using the `globals` package, for the files that need it.
4. If you extend `@eslint/js` recommended rules, switch on `no-unassigned-vars`, `no-useless-assignment` and `preserve-caught-error` now. All three already exist in ESLint 9.39, so you fix their reports on your own schedule. The create-vue setup above does not include that preset and is not affected.

When the peer ranges allow it, the upgrade is one command:

```
npm install -D eslint@10 eslint-plugin-vue@latest @vue/eslint-config-typescript@latest
```

Then run `npx eslint .` and read the report before you run `npm run lint`: the create-vue script passes `--fix`, so it changes files as it lints.

## Nuxt projects with @nuxt/eslint

The `@nuxt/eslint` module generates its config and installs its own copy of eslint-plugin-vue. Version 1.15.0, published on 10 February, already lists ESLint 10 in its peer range. The eslint-plugin-vue 10.7 it installs does not, so the Vue rules inside would run on a major their authors have not declared support for. Wait for an eslint-plugin-vue release with `^10.0.0` in its range, then upgrade ESLint and the module together. The preparation list above applies to a Nuxt project as it is.

**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/eslint-10-with-eslint-plugin-vue#webpage","@type":"WebPage","dateModified":"2026-10-27T19:31:40.253Z","datePublished":"2026-02-12","description":"ESLint 10 removes eslintrc and old Node versions. Vue's lint plugins do not list it as supported yet, so here is what to fix now and when to upgrade.","name":"ESLint 10 with eslint-plugin-vue: the upgrade","url":"https://vuejs.school/community/blog/eslint-10-with-eslint-plugin-vue","breadcrumb":{"@id":"https://vuejs.school/community/blog/eslint-10-with-eslint-plugin-vue#breadcrumb"},"isPartOf":{"@id":"https://vuejs.school/#website"},"potentialAction":[{"@type":"ReadAction","target":["https://vuejs.school/community/blog/eslint-10-with-eslint-plugin-vue"]}],"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/eslint-10-with-eslint-plugin-vue#article","dateModified":"2026-10-27T19:31:40.253Z","datePublished":"2026-02-12","description":"ESLint 10 removes eslintrc and old Node versions. Vue's lint plugins do not list it as supported yet, so here is what to fix now and when to upgrade.","headline":"ESLint 10 with eslint-plugin-vue: the upgrade","inLanguage":"en","isAccessibleForFree":true,"thumbnailUrl":"https://vuejs.school/_og/d/a_~RVNMaW50IDEwIHdpdGggZXNsaW50LXBsdWdpbi12dWU6IHRoZSB1cGdyYWRlIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~RVNMaW50IDEwIHdpdGggZXNsaW50LXBsdWdpbi12dWU6IHRoZSB1cGdyYWRl%2Cdescription_~RVNMaW50IDEwIHJlbW92ZXMgZXNsaW50cmMgYW5kIG9sZCBOb2RlIHZlcnNpb25zLiBWdWUncyBsaW50IHBsdWdpbnMgZG8gbm90IGxpc3QgaXQgYXMgc3VwcG9ydGVkIHlldCwgc28gaGVyZSBpcyB3aGF0IHRvIGZpeCBub3cgYW5kIHdoZW4gdG8gdXBncmFkZS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy9lc2xpbnQtMTAtd2l0aC1lc2xpbnQtcGx1Z2luLXZ1ZSI%2Cs_nwynLV6-vkcp1tha.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/eslint-10-with-eslint-plugin-vue#webpage"},"mainEntityOfPage":{"@id":"https://vuejs.school/community/blog/eslint-10-with-eslint-plugin-vue#webpage"},"publisher":{"@id":"https://vuejs.school/#identity"}},{"@id":"https://vuejs.school/community/blog/eslint-10-with-eslint-plugin-vue#breadcrumb","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","name":"Blog","item":"https://vuejs.school/community/blog","position":1},{"@type":"ListItem","name":"ESLint 10 with eslint-plugin-vue: the upgrade","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_~RVNMaW50IDEwIHdpdGggZXNsaW50LXBsdWdpbi12dWU6IHRoZSB1cGdyYWRlIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~RVNMaW50IDEwIHdpdGggZXNsaW50LXBsdWdpbi12dWU6IHRoZSB1cGdyYWRl%2Cdescription_~RVNMaW50IDEwIHJlbW92ZXMgZXNsaW50cmMgYW5kIG9sZCBOb2RlIHZlcnNpb25zLiBWdWUncyBsaW50IHBsdWdpbnMgZG8gbm90IGxpc3QgaXQgYXMgc3VwcG9ydGVkIHlldCwgc28gaGVyZSBpcyB3aGF0IHRvIGZpeCBub3cgYW5kIHdoZW4gdG8gdXBncmFkZS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy9lc2xpbnQtMTAtd2l0aC1lc2xpbnQtcGx1Z2luLXZ1ZSI%2Cs_nwynLV6-vkcp1tha.png","inLanguage":"en","url":"https://vuejs.school/_og/d/a_~RVNMaW50IDEwIHdpdGggZXNsaW50LXBsdWdpbi12dWU6IHRoZSB1cGdyYWRlIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~RVNMaW50IDEwIHdpdGggZXNsaW50LXBsdWdpbi12dWU6IHRoZSB1cGdyYWRl%2Cdescription_~RVNMaW50IDEwIHJlbW92ZXMgZXNsaW50cmMgYW5kIG9sZCBOb2RlIHZlcnNpb25zLiBWdWUncyBsaW50IHBsdWdpbnMgZG8gbm90IGxpc3QgaXQgYXMgc3VwcG9ydGVkIHlldCwgc28gaGVyZSBpcyB3aGF0IHRvIGZpeCBub3cgYW5kIHdoZW4gdG8gdXBncmFkZS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy9lc2xpbnQtMTAtd2l0aC1lc2xpbnQtcGx1Z2luLXZ1ZSI%2Cs_nwynLV6-vkcp1tha.png"}]}
```