We recently completed the update of the Tornado UI from Vue 2 to Vue 3. This was a major upgrade that required careful planning and execution. To ensure a smooth transition, we followed the official migration guide provided by the Vue team, with some modifications to adapt the steps to our codebase, such as using a library for class decorators instead of fully switching to the Composition API.
In addition, we migrated from Vuex to Pinia for state management, our testing framework from Jest to Vitest, and Vue-CLI to Vite. Another huge part of our effort was in updating other essential libraries to be compatible with Vue 3, and dealing with breaking changes and version incompatibilities.
Each step was crucial to maintaining the stability and performance of our application, and allowed us to introduce a new JSON editor with error hinting, better syntax highlighting, and an improved UI for easier editing.
We assumed a single-step update approach would be the best way to perform the upgrade, but we wondered if anyone had ever tried to accomplish it gradually. Fortunately, my colleague Marco and I attended the VueJS Amsterdam 2025 conference in the Netherlands and listened to a talk given by Yauheni Prakopchyk titled “Granular Update to Vue 3 for Big Projects“.
This talk led us to also consider other techniques for doing the upgrade, as well as appreciate the inventiveness of this particular developer.
He started from a simple idea: is it possible to use both Vue 2 and Vue 3 together in the same project? Spoiler: it’s “madness” as he stated, and yet he managed to make it work, albeit with some compromises.
We can say that there are three possible approaches for the transition from Vue 2 to Vue 3, and all of them have their various pros and cons.
As I wrote earlier, it’s recommended that you follow the migration guides for the core and the libraries, and tackle the breaking changes.
The cons of this method are that it takes a while – exactly how long depends on the size of the project – and must be completed all at once, which is tough for the QA team.
On the other hand, the pros are that it will require focused development, and there is no impact on the end users. This solution is the best when performance is important and there’s no need for incremental upgrades.
This way involves using the Vue 2 project as a wrapper, while migrating some parts to Vue 3 on another domain. The migrated parts are then inserted using iframes in the wrapper, until all parts are finally migrated.
The cons include the performance overhead caused by the iframes, serialized communication to/from them, and potential CORS issues. The pros are the possibility of incremental upgrades, and no conflicts with CSS and JavaScript. This approach is better when avoiding regressions is important and performance is not the main concern.
This procedure implies the use of the Vite plugin vite-plugin-federation, which requires two projects forming a “host/remote” pair: the host side is a new, empty Vue 3 project which is the top level application, the remote side is the current Vue 2 application bundled inside it, and the host will assemble some bundled parts into itself. Both the projects have to install the plugin.
As for the example mentioned by the speaker, we can expose just one function that creates the remote application and returns the router, then we can create a new instance of it to use inside the host application. We now need to synchronize the two routers (we can use the host router in history
mode and the remote router in memory
mode, see the documentation on “Different History modes”) and that’s it, we now have the old application inside the new one.
For the styles, it’s suggested to use CSS scoping with the autoprefixer
package on the remote, in order to apply a prefix for the components on that side. Thus certain styles will affect these parts only, avoiding any conflicts.
There are some facts to consider. The cons:
But we have some pros as well:
In his speech Yauheni Prakopchyk remains of the opinion that the single-step transition remains the ideal solution, and I agree with him. But now we know that module federation and CSS scoping both offer an effective solution for the transition from Vue 2 to Vue 3, if you can bear their downsides.
Did you find this article interesting? Does it match your skill set? Programming is at the heart of how we develop customized solutions. In fact, we’re currently hiring for roles just like this and others here at Würth Phoenix.