Migrating a 50+ Component Admin Portal from Vue 2 to Vue 3
Lessons from migrating a production admin portal to Vue 3 — dependency mapping, mixin refactoring, incremental adoption strategy, and what I'd do differently.
Our admin portal had grown for years with Vue 2 and the Options API. Fifteen global mixins with complex interdependencies, 8 class components using vue-property-decorator, 27 custom directives, and 50+ components across 3 application modules. Vue 2 was approaching EOL, and TypeScript support was painful. The migration wasn't optional.
Strategy: Incremental, Not Big Bang
We used the Vue 2 → Vue 3 compatibility build to run both versions side-by-side. The key was a strict dependency graph: we mapped every component's dependencies and migrated leaf nodes first, working inward. A custom script revealed hidden circular dependencies in our mixin composition that would have broken silently in Vue 3.
The three-phase approach:
- Audit and refactor: Built a dependency graph of the entire component tree. Refactored circular mixin dependencies while still on Vue 2.
- Upgrade infrastructure: Updated Vue CLI to Vite, enabled TypeScript strict mode, configured the compatibility build. Two weeks of zero user-facing changes.
- Migrate components: Utility composables first, then leaf components, then containers. Each migration: extract logic into composables, replace mixins, convert to setup syntax.
Key Lessons
- Mixins are the primary migration challenge. They hide dependencies in ways that break silently. Audit them first, replace them with composables while still on Vue 2.
- TypeScript strict mode catches migration bugs the compatibility build misses. Enable it before migrating components.
- Playwright tests running against both builds verify identical behavior before and after migration.
- Composables are not just better mixins — they're a fundamentally different composition model. Train your team before the migration starts.
The migration took 4 months alongside feature development. We shipped zero Vue 3-related production bugs. Component complexity reduced by 30%. TypeScript adoption went from 40% to 95%. The team unanimously prefers the new architecture.