University of Jena: Vue 3 Migration
Migrating nine physics-faculty institute sites from Vue 2 to Vue 3, and making the shared component library the standard again.
Nine institute sites ran on a Vue 2 codebase nearing end of life, with a shared component library that had drifted into inconsistent, chaotic use.
- role:
- Frontend developer; drove the migration strategy, testing, and component library adoption
- stack:
- Vue 3 · Vuex · Cypress · Webpack · Axios · SCSS · Tailwind CSS
- when:
- 2019–2022
- published:
Problem & constraints
Friedrich-Schiller-Universität Jena’s web presence spans more than a hundred departments. My slice of it was the Physikalisch-Astronomische Fakultät: nine institute websites, from the Astrophysical Institute and University Observatory to the Otto Schott Institute of Materials Research and the Fraunhofer Institute for Applied Optics and Precision Engineering (IOF).
The sites ran on Vue 2, which was approaching end of life, and every year of growth made the codebase harder to maintain. The framework wasn’t the only problem: a shared component library existed, but adoption across the sites was inconsistent (some teams used it, some didn’t), so it had drifted into an obsolete, chaotic state with per-site variants of the same UI.
Two constraints shaped everything. The sites had to stay live throughout; a university can’t take its institutes offline for a rewrite. And this was a cross-functional team (backend developers, UI/UX designers, DevOps engineers), so the migration had to move without blocking anyone else’s work. The Vue migration itself, its testing, and the component library were my responsibility.
Architecture
These were not SPAs. Each institute site was a multi-page application with Vue mounted where interactivity was needed, on dashboards and interactive components, built with Webpack. State lived in Vuex, and data came over RESTful APIs (via Axios) from a plain Java backend. The shared component library sat across all nine sites.
The migration itself ran in a separate repository: the codebase was converted there, incrementally and under test, while production stayed untouched. Only once the converted code was thoroughly tested did it replace the live sites; there was never a cutover of a half-migrated codebase. The decisions behind it are logged below, each with the alternative it beat.
Outcomes
Tree-shaking and Vue 3’s smaller runtime cut the JavaScript bundle by 40%. Accessibility improved to WCAG compliance through ARIA attributes and semantic HTML, and Cypress-based integration tests, introduced together with the team, gave the migration end-to-end coverage.
Just as important were the structural wins: the platform could keep up with current Vue features again, old hand-rolled logic was replaced with well-supported Vue ecosystem addons, the component library was consistent and current across all nine sites, and the codebase came out of the migration noticeably cleaner than it went in.
Lessons
If I did it again, I would convert everything to the Composition API and leave no Options API behind. Keeping the stable sections felt pragmatic at the time, but a mixed-style codebase carries a quiet cost: every future upgrade and every new contributor has to deal with two mental models, and the leftover Options code is exactly where future friction will surface. The incremental, separate-repo strategy, on the other hand, I would repeat without hesitation: it’s what let nine live university sites migrate frameworks with no drama.
Key decisions
01 Migration strategy
Incremental conversion in a separate repository.
Nine public institute sites had to stay online, so a parallel repo could be converted and tested piece by piece, and only replace production once it was proven.
The live sites never served half-migrated code; the swap happened once, after the converted codebase had been thoroughly tested.
rejected: A big-bang rewrite on the live codebase
02 Options API vs Composition API
Composition API as the default, keeping Options API where it still worked fine.
Most of the logic genuinely gained modularity from the rewrite, but stable Options-API sections weren't worth the extra churn within the migration's scope.
This is also my biggest lesson from the project; see Lessons above.
rejected: Forcing a wholesale conversion of every component
03 State management
Fixing the existing Vuex implementation.
Vuex was already wired in but used incorrectly, and correcting the store patterns recovered its value without stacking a second migration on top of the framework one.
rejected: Ripping it out for a different store
04 Shared component library
Reviving the shared library and making it the default across all nine sites.
Inconsistent adoption was exactly what had made the library obsolete and chaotic; consolidating meant one place to update and a consistent UI everywhere.
rejected: Letting each site keep its local component variants
Key figures
| Metric | Value | Status |
|---|---|---|
| Institute sites migrated | 9 | reported |
| JS bundle size | −40% | reported |
Reported: The faculty's institute sites in the migration's scope, 2019–2022 · Production JS bundle from the build output, before vs after the Vue 3 cutover