ESLint, the linter that flags mistakes in your JavaScript and Vue files, published version 10.0.0 on 6 February. The release post and its migration guide describe a release that finishes a move started two majors ago: the old .eslintrc system is gone, and flat config, the single eslint.config.js file, is all that is left. A Vue project set up in the last year already uses it.
For a Vue and TypeScript project, the advice this week is to wait. Six days after the release, eslint-plugin-vue, typescript-eslint and the packages around them do not yet list ESLint 10 as supported. Stay on ESLint 9.39, do the clean-up ESLint 10 needs now, and the upgrade becomes a single install once the plugins catch up.
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.0or 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.eslintignorefiles are no longer read, theESLINT_USE_FLAT_CONFIGvariable is ignored, and the CLI flags that only made sense for the old system are gone:--no-eslintrc,--env,--resolve-plugins-relative-to,--rulesdirand--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:recommendedgained three rules:no-unassigned-vars,no-useless-assignmentandpreserve-caught-error.- JSX references are tracked, so
no-unused-varsandno-undefunderstand<Card />in.tsxfiles. - 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-vue10.7.0, from 16 January: ESLint^8.57.0 || ^9.0.0.vue-eslint-parser10.2.0, the parser that reads.vuefiles for it: the same range.typescript-eslint8.55.0, from 9 February: the same range.@vue/eslint-config-typescript14.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.
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:
{
"scripts": {
"lint": "eslint . --ext .vue,.ts --ignore-path .gitignore"
}
}
{
"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 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:
- Check
node --versionlocally, in CI and in your editor. Anything outside ESLint 10's range (^20.19.0,^22.13.0or 24 and newer) has to move first. - Delete any leftover
.eslintrc.*or.eslintignorefile. ESLint 9 with flat config already ignored them, so removing them changes nothing except the confusion. - Search for
eslint-envcomments and replace each with alanguageOptions.globalsentry, using theglobalspackage, for the files that need it. - If you extend
@eslint/jsrecommended rules, switch onno-unassigned-vars,no-useless-assignmentandpreserve-caught-errornow. 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.