Vue 2 to Vue 3 migration notes from product work
The migration risks I watch for: component contracts, reactivity assumptions, plugins, tests, and design-system drift.
A Vue 2 to Vue 3 migration is rarely just a framework migration. In product code, it is a test of how well the team understands its component contracts, state assumptions, plugin dependencies, and user-critical flows.
The dangerous version of the migration starts with "let's rewrite everything with the Composition API." That sounds modern, but it often turns a manageable upgrade into a product-risk event.
The safer version starts with inventory.
Inventory first
Before changing code, make the product surface visible:
- Which screens generate revenue or operational risk?
- Which components are reused across many flows?
- Which plugins block Vue 3 compatibility?
- Where does the app rely on global event buses, filters, mixins, or legacy lifecycle behavior?
- Which tests prove user-critical behavior?
This inventory gives the migration a risk map. Without it, the team usually discovers risk by breaking things.
Start with leaf components
Leaf components are low-risk places to learn. They have few dependencies and limited blast radius: badges, cards, presentational rows, small display components.
Migrating these first teaches the team how Vue 3 behaves in the actual codebase. It also reveals tooling gaps: TypeScript issues, build warnings, test setup, lint rules, or style regressions.
Do not start with global layout, shared forms, tables, or stateful workflow components. Those are the places where small behavior changes spread widely.
The Composition API is a tool, not a mandate
The Composition API is valuable when logic needs clearer grouping, reuse, or testability. It is not automatically better for every component.
A simple Options API component that renders clearly may not need a rewrite. Migration is already enough change. Do not add abstraction pressure unless it solves a real problem.
Good candidates for Composition API refactors:
- components with tangled watchers and lifecycle behavior
- forms with reusable validation logic
- tables with filtering, pagination, and URL state
- flows where state needs to be tested outside the component
Bad candidates:
- simple presentational components
- components already easy to read
- files being changed only because the migration makes rewriting feel tempting
Watch the sharp edges
The common migration problems are not always in the obvious places.
v-model behavior: Vue 3 changes the default model prop and event. Any custom input component deserves careful review.
Watchers: Reactivity changes can expose assumptions around nested objects, arrays, and timing.
Plugins: Router, store, i18n, analytics, and UI libraries may each have separate migration paths.
Event contracts: Components that emit events to parent workflows can silently change behavior if names, payloads, or timing shift.
Design-system components: A tiny prop or slot behavior change can affect dozens of product screens.
v-model, validation, focus, and disabled states.
Filters, pagination, empty states, async data.
Small changes propagate across the product.
Test product behavior, not migration ceremony
A migration can pass component-level tests and still break the product. Prioritize flows users care about:
- Can a merchant complete onboarding?
- Can an admin save settings?
- Can a table filter, paginate, and recover from empty results?
- Can a modal trap focus and close correctly?
- Can a form handle validation, submit, and error states?
Those checks matter more than proving every component uses the newest style.
Keep the migration boring to users
The best framework migration is invisible. Users should get a faster, more maintainable product without feeling the framework changed underneath them.
That means avoiding opportunistic redesigns. It also means separating migration PRs from feature PRs when possible. If the UI changes, reviewers need to know whether it changed because the product needed it or because the migration accidentally shifted behavior.
A practical rollout shape
The migration plan I trust looks like this:
- Inventory flows, shared components, and plugin blockers.
- Upgrade tooling and test setup.
- Migrate low-risk leaf components.
- Migrate shared primitives with visual checks.
- Migrate forms, tables, and stateful workflows.
- Verify critical product flows in browser.
- Remove compatibility shims only after the product is stable.
This order respects how product risk actually moves through an application.
Communication matters
Framework migrations create anxiety because they are expensive and often invisible to users. Stakeholders may wonder why a team is spending weeks on something that does not create a new feature. Engineers may want to use the migration as a chance to clean everything. Designers may notice small UI regressions and lose confidence.
The communication plan should be explicit. Explain which risks the migration reduces: old dependencies, unsupported plugins, slower build tooling, fragile component contracts, or blocked hiring because the stack is stale. Then explain how the team will prevent user-facing regressions.
I also like publishing a migration scorecard:
- components migrated
- critical flows verified
- plugin blockers remaining
- known visual regressions
- compatibility shims still active
That scorecard turns a vague technical project into visible product risk management.
Watch for design-system drift
One migration risk is visual drift disguised as technical cleanup. A developer touches a shared component, notices old spacing, and "fixes" it. Another updates a slot structure and changes how empty states render. A third replaces a deprecated UI library primitive and slightly alters focus behavior.
Each change may be reasonable in isolation, but together they create a product that feels different without a design decision behind it. I prefer capturing visual baselines for shared components and key screens before the migration starts. Screenshots, Storybook states, or Playwright snapshots can all help. The point is not pixel perfection. The point is knowing whether the UI changed intentionally.
Vue 3 is a strong upgrade. The work is worth doing. But in product systems, the framework is not the customer. The migration succeeds when the product gets healthier and users do not pay the transition cost.