Vue 2 reached end of life on 31 December 2023. Vuetify 2 followed on 25 January 2025. If your admin panel still runs on them, it gets no security fixes, and every SOC 2 or ISO 27001 review asks why.
To show what the move involves, I migrated a well-known open-source app in public: Creative Tim's Vuetify Material Dashboard (MIT, 1.3k GitHub stars). The full branch is here: https://github.com/SayedEsmail/vuetify-material-dashboard. Every commit handles one concern and lists the breaking changes it fixed, so the commit log reads as the migration plan. I wrote the plan, did the work with an AI coding assistant, and reviewed every change myself at the end.
The starting point
- Vue 2.6, Vuetify 2.2, Vuex 3, Vue Router 3, Vue CLI 4 on webpack 4.
- 33 single-file components and 5,620 lines of JavaScript and Vue.
- 553 lines of custom SASS written against Vuetify 2's internal class names.
The target was Vue 3.5, Vuetify 3, Pinia, Vue Router 4 and Vite.
Step 0: delete before you migrate
Seven of the 33 components could not be reached. Five views had no route, and two components were only used by those views. A form-validation library was registered globally and never used. The test suite still asserted the Vue CLI scaffold text, so it could never pass.
Deleting that first removed a third of the code before any migration work started. It is the cheapest saving in every migration, and an audit finds it in an afternoon.
Why Vuetify 2 projects can't migrate "gradually"
The usual Vue 2 path is the @vue/compat build: run on Vue 3 in compatibility mode, then fix warnings one by one. That path is closed when the app uses Vuetify 2, because Vuetify 2 cannot run on Vue 3 at all. The UI layer has to move in one step.
So the order matters:
- Tooling first: Vue CLI to Vite, then the new dependency set.
- App bootstrap:
createApp,createVuetify, the i18n adapter. - State and routing: Vuex to Pinia, Router 3 to 4. These are mechanical.
- Vue-2-only plugins: the chart wrapper had no Vue 3 version, so it became a 56-line local component on Chartist 1.x.
- Vuetify, from the outside in: layout shell, then the shared component library, then the views.
- The custom CSS layer last.
Where the real work is
Component APIs changed a lot, but those changes are well documented: v-list-item-content is gone, activator slots give props instead of on and attrs, v-tabs-items became v-window. I hit 43 distinct breaking changes and logged each one. That list is now the checklist I run in every audit.
The expensive surprises were elsewhere:
- Custom CSS on framework internals. 470 lines targeted classes like
.theme--light,.v-size--defaultand.v-card__text. Not one of them exists in Vuetify 3. Nothing errors. The styles silently stop applying. - Behaviour that changed without an error. In Vuetify 3, a navigation item linked to
/stays highlighted on every page unless you mark itexact. Alerts now clip anything positioned outside them. The build stays green and the UI is subtly wrong. - Bugs that were already there. The theme colour picker never reached dark mode because it read a property that was never defined. Turning the sidebar image back on stored the value
trueinstead of an image URL. Neither was caused by the migration, but a migration is when someone finally reads every line.
The result
- Vue 3.5, Vuetify 3.13, Pinia, Vue Router 4, Vite 8, ESLint 10.
- 26 components, 3,644 lines of JavaScript and Vue, and 350 lines of SASS in one file.
- The same 8 routes and the same behaviour, minus the two bugs above.
- A test suite that runs: every route renders in the full layout with zero Vue warnings.
What I'd tell a team planning this
- Run an inventory first. Dead code, undeclared dependencies and tests that can't pass are common, and they change the scope.
- Budget for CSS and visual QA, not only for component syntax.
- If you use Vuetify 2, plan the UI layer as one cut-over, behind a feature branch, with route-level smoke tests in place before you start.
- Decide the target versions up front. Vuetify 4 is out now. Going to Vuetify 3 first is still the shorter step from Vuetify 2, and 3 to 4 is a much smaller change.
If your product still runs on Vue 2, Nuxt 2 or Vuetify 2, I do a fixed-price migration readiness audit: a written report with the dead code, the breaking changes that apply to your codebase, the risks, and a fixed quote for the migration.
Sayed Esmail · https://github.com/SayedEsmail · https://linkedin.com/in/sayed-esmail-khattab
