I'm Johnson, the author of Volar (now Vue Language Tools). We released 2.0 in March 2024. This is a look back at what we changed, why, and what it cost.
Vetur and Volar v1 implemented Vue's IDE support through the Language Server Protocol. For small and medium projects this worked fine. For larger projects it broke down, and the reason was always the same: memory.
TypeScript Server and the Vue Language Server each kept their own copy of the project's TS AST — every file, every .d.ts in node_modules. When a project pulled in thousands of .d.ts files (common for anything with a big dependency tree), the two processes together could exhaust available memory.
TS Server ██████████ 516 MB
Vue Language Server ███████████████ 738 MB
─────────────
Total: 1254 MB
It got worse. To make .ts files recognize .vue component types, and to support rename-across-files from .ts, users also had to install the TypeScript Vue Plugin — doubling the footprint again.
TS Server + Plugin ████████████████████ much bigger
Vue Language Server ███████████████ 738 MB
─────────────────
(nearly doubled)
The first fix I shipped was Takeover Mode. The idea was simple: if you manually disabled VSCode's built-in TypeScript extension in your Vue workspace, the Vue Language Server took over IDE support for .ts files too. One language server instead of two.
TS Server (disabled)
Vue Language Server ███████████████ 738 MB
────────────
Total: 738 MB
On paper, it solved the memory problem. In practice, Takeover Mode was barely used.
The discoverability was bad. To enable it, you had to:
- Find the list of built-in extensions in VSCode
- Locate "TypeScript and JavaScript Language Features"
- Disable it — per workspace, for every Vue project
If you didn't read Vue's documentation, you didn't know Takeover Mode existed. Even users who knew often didn't bother.
And the mode had deeper limits I kept running into:
- No support for TypeScript LS Plugins
- Features lagged behind VSCode's TypeScript integration
- Every TS update risked a performance regression, because the TS auto-import cache kept changing shape
- Invalidating that cache from our side was hard
It was a workaround. I spent a lot of v1 trying to make a workaround feel like a fix.
In August 2023 I started asking a different question. If the TypeScript Vue Plugin could proxy TS functionality, why not put all TS features for Vue files inside that plugin?
TypeScript support for Vue files would come from the TS Server via the plugin. Everything else (CSS, HTML, JSON) would come from the Vue Language Server. Takeover Mode in reverse — except this time the user doesn't have to do anything.
The Vue Language Server no longer needs its own TypeScript instance. Memory usage lands in line with v1 Takeover Mode, without the configuration cost.
┌────────────┐
│ Editor │
└──┬──────┬──┘
│ │
┌────┘ └────┐
▼ ▼
┌───────────────┐ ┌─────────────────────┐
│ TS Server │ │ Vue Language Server │
│ │ │ │
│ ┌─────────┐ │ │ CSS / HTML / JSON │
│ │ Vue TS │ │ │ │
│ │ Plugin │ │ │ (no ts.Program) │
│ └─────────┘ │ │ │
└───────────────┘ └─────────────────────┘
TS Server: 516 MB Vue LS: 123 MB
Total: 639 MB
I wrote the PoC over four months.
In January 2024 we released Volar.js 2.0, carrying the infrastructure and refactoring Hybrid Mode required. The TS plugin migration for Vue went in as one PR.
The benchmark tells the cleanest part of the story:
| TS Server | Vue Language Server | Total | |
|---|---|---|---|
| v1 without Takeover Mode | 516 MB | 541 MB + 197 MB | 1254 MB |
| v1 with Takeover Mode | — | 541 MB + 197 MB | 738 MB |
| v2 with Hybrid Mode | 516 MB | 123 MB | 639 MB |
(Measured on elk-zone/elk.)
We thought we were ready.
Being the first language tool to go all-in on TS plugins meant being the first to hit a long list of compatibility issues our internal testing hadn't surfaced. One bug traced back to the Node.js version bundled with VSCode itself — which meant we couldn't fix it on our end. We had to wait a month for VSCode to ship an update.
I worked through every stability issue I could reproduce. User complaints kept coming for months.
By the time I'm writing this, we're on 2.0.26. Performance is meaningfully better than v1; memory usage is slightly lower than v1 Takeover Mode, without the configuration friction. The experience we set out to build is the experience users get now.
No prior open-source project had integrated with the TS Server at this depth. Every decision had to be made from first principles. Seven months of it, August 2023 to March 2024.
More frameworks are migrating to TS plugin integration through Volar.js now — the direction was right.
Full-time open source only stays sustainable if it's funded. If Vue Language Tools is part of your team's workflow, please consider sponsoring this work.
This post only covered Hybrid Mode. 2.0 included a lot of other work — the vue-tsc rewrite drastically reduced memory usage, among other things — which I'll write about separately.
Huge thanks to StackBlitz, who have supported my full-time development since early 2023.
Huge thanks to Astro and JetBrains for ten thousand dollars in donations that let me focus entirely on shipping this.
And to everyone else listed below — you made the difference.
Thank you for all the effort and passion that you and the other developers put into this awesome tool and the vue/frontend ecosystem itself! ❤️
Sometimes it can be hard to get things working as expected and it can be exhausting, but if you get it working its one of the best feeling out there. Thats one reason that make it so much fun to be a developer and push your limits every day and be a better version of you tomorrow then you was today.