Vue 2 → Vue 3 Migration

The Practical Differences You Care About

Vue 2 → Vue 3 Migration

If you're moving a Vue 2 app, the migration build plus a few mechanical changes get you 80% of the way there.

4 min read Level 2/5 #vue#migration#vue2
What you'll learn
  • Use the @vue/compat migration build
  • Replace filters with methods or computeds
  • Update v-model to modelValue

Migrating from Vue 2 isn’t a rewrite. Most components run unchanged. The biggest changes are the new reactivity system, v-model contract, and a few removed features.

Migration Build

@vue/compat is Vue 3 with Vue 2 behavior turned on. It logs warnings for every incompatibility so you can fix incrementally.

npm i vue@^3 @vue/compat
// vite.config.ts
export default {
  resolve: {
    alias: { vue: '@vue/compat' },
  },
}

Flip features one at a time as your code is ready.

Filters Are Gone

Replace {{ price | currency }} with a method or computed.

<!-- before -->
<p>{{ price | currency }}</p>

<!-- after -->
<p>{{ formatCurrency(price) }}</p>

v-model on Components

Vue 2 used value + input; Vue 3 uses modelValue + update:modelValue.

<!-- Vue 2 child -->
<script>
export default {
  props: ['value'],
  methods: { update(v) { this.$emit('input', v) } },
}
</script>

<!-- Vue 3 child -->
<script setup lang="ts">
defineProps<{ modelValue: string }>()
const emit = defineEmits<{ 'update:modelValue': [string] }>()
</script>

Vue 3 supports multiple v-models too: v-model:title, v-model:body.

Other Changes

  • Vue.extend, Vue.component, Vue.directive → use app.component, app.directive on the app instance.
  • $on/$off/$once removed (event bus pattern — use a library like mitt).
  • Async components: use defineAsyncComponent.
  • Functional components: just plain functions, no functional: true.
  • Reactive arrays: index assignment now reacts naturally — no more Vue.set.

Composition API in Vue 2.7

If you’re still on Vue 2, upgrade to 2.7 — it backports the Composition API and <script setup>. You can start writing modern code today and migrate to Vue 3 later.

Going Further →