Skip to content
Release notesTypeScript

Vue Language Tools 3.0: vue-tsc 3 and the always-on hybrid mode

Vue Language Tools 3.0.0 was published on 1 July, with the Vue (Official) extension, vue-tsc and the language server all moving together. Most projects need a dependency bump and a settings rename, nothing more.

vuejs.school team3 Jul 20257 min readvue-tsc 3.0 · TypeScript 5.8

Vue Language Tools is the project behind everything that understands .vue files in your editor and on the command line: the Vue (Official) extension for VS Code, the @vue/language-server package other editors use, the @vue/typescript-plugin that teaches TypeScript's own server about templates, and vue-tsc, the command that type checks a Vue project. All four moved to 3.0.0 on 1 July.

On VS Code, the upgrade is an extension update, a vue-tsc bump and a few renamed settings. On Neovim, Zed and other editors, your editor's language client has one breaking change to handle before you upgrade the language server.

What shipped

The release notes group the changes under stability, developer experience, new options and renamed settings. Four of the changes affect your configuration:

  • Hybrid mode is always on. The option to disable it is gone, along with the code paths that existed to support it.
  • The connection to TypeScript's server was rebuilt. The named pipe that 2.x used is removed, and requests are forwarded through the editor client instead. The VS Code extension does this for you; other editors have to add it.
  • Three settings are renamed from vue.complete.* to vue.suggest.*.
  • Four new vueCompilerOptions: strictVModel, strictCssModules, strictSlotChildren and resolveStyleImports.

The rest comes with the update:

  • The language server now depends on exact versions of the Volar packages it is built on, so a fresh install from npm behaves the same as the bundled extension.
  • A race condition when the TypeScript and Vue extensions started at the same time is fixed.
  • "Go to definition" on a globally registered component jumps to the component's source file rather than a type declaration.
  • TypeScript's sortImports and removeUnusedImports commands work in .vue files, and template refs are now document links.
  • The VS Code extension is translated into Simplified and Traditional Chinese, Russian and Japanese, and it no longer adds items to the status bar.
  • The changelog adds a long list of type inference fixes in templates, for refs, slots, models and props.

The changelog also lists one removal: support for the experimental defineProp macro (singular, not defineProps) is dropped.

Hybrid mode is now the only mode

A .vue file is really three languages in one file. In 1.x, the Vue server did all of the work itself, including the TypeScript, and you could switch VS Code's built-in TypeScript off in a mode called "takeover". Version 2.0 introduced hybrid mode: the Vue server handles the template and style blocks, and TypeScript's own tsserver, with the Vue TypeScript plugin loaded into it, handles the TypeScript. That meant one TypeScript process for .ts and .vue files, with the same memory and the same results.

Version 2 kept an escape hatch. The vue.server.hybridMode setting accepted auto, true, false and typeScriptPluginOnly. With auto, the default, the extension turned hybrid mode off, and said so in a notice, when it found a TypeScript plugin from another extension that it did not know to be compatible, or a TypeScript version older than 5.3.

Two modes meant two sets of behaviour to debug. The pull request that removed the option says the goal is to stop patching VS Code's TypeScript extension from the outside altogether. In 3.0 the setting does not exist.

If 2.x ever told you "Hybrid Mode is disabled automatically" because of another extension, that extension's TypeScript plugin now runs next to Vue's. If Vue features misbehave, disable that extension for your Vue projects.

What to do in VS Code

Install or update one extension, Vue (Official), which needs VS Code 1.88 or newer. If you still have "TypeScript Vue Plugin (Volar)" installed from the 1.x era, uninstall it. The plugin has been bundled inside the main extension since 2.0, and the extension registers it for both VS Code's bundled TypeScript and a workspace TypeScript version. You do not need to pick a TypeScript version to get Vue support.

Then open your settings. Three keys are renamed, and the casing values changed names with them:

.vscode/settings.jsonBreaks
{
  "vue.server.hybridMode": "auto",
  "vue.complete.casing.tags": "autoPascal",
  "vue.complete.casing.props": "autoKebab",
  "vue.complete.defineAssignment": true
}
.vscode/settings.jsonWorks
{
  "vue.suggest.componentNameCasing": "preferPascalCase",
  "vue.suggest.propNameCasing": "preferKebabCase",
  "vue.suggest.defineAssignment": true
}

The old auto… values become prefer…Case, and the fixed kebab, pascal and camel values become always…Case. The three values shown are the defaults, so if you never changed them you can delete the keys rather than rename them.

Version 3.0 also removes the "doctor" check, the split editor and the command that wrote virtual files to disk. Their settings are gone, and so are a few others. VS Code shows the old keys as unknown, and deleting them is the fix:

  • vue.server.hybridMode and vue.server.compatibleExtensions
  • vue.server.maxOldSpaceSize
  • vue.doctor.status and vue.splitEditors.*
  • vue.codeLens.enabled and vue.codeActions.enabled
  • vue.updateImportsOnFileMove.enabled

vue.inlayHints.*, vue.format.* and vue.autoInsert.* keep their names and defaults.

Upgrade vue-tsc and rerun the type check

vue-tsc is the command behind the type-check script in a create-vue project. It is how you type check templates from the command line, because vite build and tsc both skip them. Upgrade it like any other dev dependency, then run it once:

npm install -D vue-tsc@3
npx vue-tsc --noEmit

The command line did not change. vue-tsc --noEmit on a single tsconfig.json, or vue-tsc --build on the project references create-vue generates (npm run type-check), both run as before.

What can change is the list of errors. The changelog lists fixes to how template expressions, refs, slots, models and props are typed, and they can move the count either way. One of them removes errors: an optional prop that has a default is no longer typed as possibly undefined when the template reads it (the bug showed up with exactOptionalPropertyTypes on). Run the type check before you commit the upgrade, and commit any fixes it asks for together with the version bump.

Four new options in tsconfig.json

The new options live under vueCompilerOptions in your tsconfig.json, next to compilerOptions, and both the editor and vue-tsc read them. In a create-vue project, that file is tsconfig.app.json (shown here without its exclude and compilerOptions):

tsconfig.app.json
{
  "extends": "@vue/tsconfig/tsconfig.dom.json",
  "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
  "vueCompilerOptions": {
    "strictVModel": true,
    "strictCssModules": true,
    "strictSlotChildren": true
  }
}

What each option does:

  • strictVModel applies stricter type constraints to v-model bindings.
  • strictCssModules type checks CSS module class names, so $style.heder is an error instead of undefined at runtime.
  • strictSlotChildren applies stricter types to what you pass as slot children.
  • resolveStyleImports generates type imports for external stylesheets loaded through <style src> or @import. Its pull request says to add a declare module '*.css' stub before you turn it on, so those imports do not break type checking.

If your vueCompilerOptions already set strictTemplates: true, strictVModel and strictSlotChildren are on by default; strictCssModules is not.

For a new project, turn on all three strict options on day one. For an existing project, enable one, fix what it finds, commit, then the next. Each one touches a different kind of code, and three at once makes the first run look worse than it is.

Other editors: forward requests to TypeScript

If you use Neovim, Zed, Emacs or anything else that talks to @vue/language-server, 3.0 has one breaking change you cannot ignore. Version 2 ran a small named pipe server so the Vue language server could reach the TypeScript server directly. The project says it caused many issues, and 3.0 removes it. Your editor's LSP client now does the relaying:

  1. It receives a tsserver/request message from the Vue server.
  2. It forwards the request to the TypeScript server.
  3. It sends the answer back to the Vue server as tsserver/response.

The typescript.tsdk initialisation option is gone as well; TypeScript is now a peer dependency the server finds on its own.

The maintainers published an upgrade guide for language server clients with a Neovim configuration as the reference. The Neovim page it links to says 3.0 works only with the vtsls TypeScript server, not ts_ls, because ts_ls does not handle the typescript.tsserverRequest command the forwarding uses. Point your TypeScript server's plugin path at the Vue language server's own folder, where @vue/typescript-plugin is installed as its dependency, so the two stay on the same version.

When to upgrade

The release notes list no known issues. A 3.0.1 patch followed on 2 July with two small fixes, one of them to v-else syntax highlighting, and an update to Volar 2.4.17.

For a Vue 3 project on VS Code, upgrade now. The change is a version bump, a settings rename and a cleaner extension, and the new strictness options catch mistakes that used to reach the browser. On another editor, stay on @vue/language-server@2 until your editor's Vue plugin ships the forwarding, because the 3.0 server does not work properly without it.

If you maintain a Vue 2 codebase, plan for the next minor. The notes announce that 3.1 will drop support for Vue 2 and vue-class-component, and the discussion announcing it names 3.0 as the last line with Vue 2 support and suggests pinning the extension to 3.0.x for Vue 2 work.

Check it stuck3 questions from the Type checking and editor setup quiz.
Practice now
Was this helpful?

Keep reading