Vue.js Upgrade Guide

Compare Vue.js versions and see exactly what changes, what requires action, and what you can start using.

53Changes
34Breaking
34Actions
13Features
1Migration tools

Breaking Changes

Component v-model API changed

Action required
breakinghigh impact

The default prop and event used by v-model on custom components changed from value and input to modelValue and update:modelValue.

Beforevue
<script>
export default {
  props: ['value'],
  methods: {
    updateValue(value) {
      this.$emit('input', value)
    }
  }
}
</script>
Aftervue
<script>
export default {
  props: ['modelValue'],
  emits: ['update:modelValue'],
  methods: {
    updateValue(value) {
      this.$emit('update:modelValue', value)
    }
  }
}
</script>
Migration

Replace the value prop with modelValue and emit update:modelValue instead of input. Replace .sync usage with v-model arguments where applicable.

Template filters were removed

Action required
breakinghigh impact

Vue 3 removed the filters syntax from templates. Expressions inside template interpolations and v-bind should use JavaScript expressions instead.

Beforevue
<template>
  <p>{{ accountBalance | currencyUSD }}</p>
</template>
Aftervue
<template>
  <p>{{ accountBalanceUSD }}</p>
</template>

<script>
export default {
  computed: {
    accountBalanceUSD() {
      return '$' + this.accountBalance
    }
  }
}
</script>
Migration

Replace template filters with methods or computed properties. If filters were registered globally, move the shared formatting logic to regular JavaScript functions or another shared utility.

Global API moved to application instances

Action required
breakinghigh impact

Vue 3 introduces createApp() and moves APIs that globally mutate Vue behavior to an application instance. Root applications are no longer created with new Vue().

Beforejavascript
import Vue from 'vue'
import App from './App.vue'

Vue.use(MyPlugin)
Vue.component('MyComponent', MyComponent)

new Vue({
  render: h => h(App)
}).$mount('#app')
Afterjavascript
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

app.use(MyPlugin)
app.component('MyComponent', MyComponent)

app.mount('#app')
Migration

Create the root application with createApp(). Move globally mutating APIs such as Vue.use(), Vue.component(), Vue.directive(), Vue.mixin() and Vue.config to the returned application instance.

Key attribute behavior changed

Action required
breakingmedium impact

Vue 3 automatically generates unique keys for conditional branches. When using <template v-for>, the key should be placed on the <template> element instead of its children.

Beforevue
<template v-for="item in items">
  <div :key="item.id">
    {{ item.name }}
  </div>
</template>
Aftervue
<template
  v-for="item in items"
  :key="item.id"
>
  <div>
    {{ item.name }}
  </div>
</template>
Migration

Remove unnecessary manual keys from v-if/v-else branches where possible. For <template v-for>, move the key to the <template> element.

v-if now takes precedence over v-for

Action required
breakinghigh impact

When v-if and v-for are used on the same element, Vue 3 evaluates v-if before v-for. Vue 2 used the opposite precedence.

Beforevue
<template>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</template>
Aftervue
<script>
export default {
  computed: {
    activeUsers() {
      return this.users.filter(
        user => user.isActive
      )
    }
  }
}
</script>

<template>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</template>
Migration

Avoid using v-if and v-for on the same element. Prefer filtering the collection with a computed property before rendering it with v-for.

v-bind merge behavior now respects declaration order

Action required
breakingmedium impact

When an element has both individual attributes and v-bind="object", Vue 3 determines which value wins based on their declaration order.

Beforevue
<div
  id="red"
  v-bind="{ id: 'blue' }"
></div>
Aftervue
<div
  v-bind="{ id: 'blue' }"
  id="red"
></div>
Migration

Review elements that combine v-bind="object" with individual attributes. In Vue 3, place the binding that should take precedence later in the attribute list.

v-on.native modifier was removed

Action required
breakinghigh impact

Vue 3 removes the .native modifier from v-on. Component-emitted events should be declared with the emits option, while undeclared listeners can fall through to the component root element.

Beforevue
<template>
  <MyComponent
    @close="handleClose"
    @click.native="handleClick"
  />
</template>
Aftervue
<template>
  <MyComponent
    @close="handleClose"
    @click="handleClick"
  />
</template>

<script>
export default {
  emits: ['close']
}
</script>
Migration

Remove .native modifiers and explicitly declare component-emitted events with the emits option. Review components that re-emit native events, because undeclared listeners can fall through to the root element.

Functional Components API changed

Action required
breakinghigh impact

Vue 3 removes the functional component option and the <template functional> SFC syntax. Functional components are now created as plain functions receiving props and context.

Beforejavascript
export default {
  functional: true,

  props: ['level'],

  render(h, { props, data, children }) {
    return h(
      `h${props.level}`,
      data,
      children
    )
  }
}
Afterjavascript
import { h } from 'vue'

const DynamicHeading = (props, context) => {
  return h(
    `h${props.level}`,
    context.attrs,
    context.slots
  )
}

DynamicHeading.props = ['level']

export default DynamicHeading
Migration

Remove functional: true and convert JavaScript functional components to plain functions. For SFCs using <template functional>, remove the functional attribute and migrate props/attrs access to the Vue 3 syntax. Consider using a normal stateful component when functional rendering is not specifically needed.

Async Components now use defineAsyncComponent

Action required
breakinghigh impact

Vue 3 introduces defineAsyncComponent() for explicitly creating async components. The component option is renamed to loader when using the advanced configuration format.

Beforejavascript
const AsyncComponent = () =>
  import('./MyComponent.vue')

export default {
  components: {
    AsyncComponent
  }
}
Afterjavascript
import { defineAsyncComponent } from 'vue'

const AsyncComponent = defineAsyncComponent(() =>
  import('./MyComponent.vue')
)

export default {
  components: {
    AsyncComponent
  }
}
Migration

Wrap async component loader functions with defineAsyncComponent(). If using the advanced async component configuration, rename the component option to loader and review loading/error component behavior.

Render Function API changed

Action required
breakinghigh impact

Vue 3 changes the render function API. The h function must be imported from Vue, VNode props use a flat structure, and registered components are resolved explicitly.

Beforejavascript
export default {
  render(h) {
    return h('div', {
      attrs: {
        id: 'app'
      },
      on: {
        click: this.handleClick
      }
    })
  }
}
Afterjavascript
import { h } from 'vue'

export default {
  render() {
    return h('div', {
      id: 'app',
      onClick: this.handleClick
    })
  }
}
Migration

Import h from vue instead of receiving it as a render argument. Update VNode props to the flat Vue 3 structure and use resolveComponent when resolving registered components by name.

Scoped slots were unified with regular slots

Action required
breakingmedium impact

Vue 3 removes $scopedSlots. All slots are exposed through $slots and are accessed as functions.

Beforejavascript
this.$scopedSlots.header
this.$slots.default
Afterjavascript
this.$slots.header()
this.$slots.default()
Migration

Replace $scopedSlots references with $slots and invoke slots as functions when accessing them programmatically.

$listeners was merged into $attrs

Action required
breakinghigh impact

Vue 3 removes the $listeners instance property. Event listeners are now included in $attrs using onXxx properties.

Beforevue
<input
  v-bind="$attrs"
  v-on="$listeners"
/>
Aftervue
<input v-bind="$attrs" />
Migration

Remove usages of $listeners. Component listeners that are not declared as emitted events are available through $attrs.

$attrs now includes class and style

Action required
breakingmedium impact

Vue 3 includes class and style attributes in $attrs, including when inheritAttrs is false.

Migration

Review components that manually forward $attrs or use inheritAttrs: false. Class and style are now included and may be forwarded together with other attributes.

Custom directive lifecycle hooks changed

Action required
breakinghigh impact

Vue 3 aligns custom directive lifecycle hooks with component lifecycle hooks and removes binding.expression.

Beforejavascript
Vue.directive('highlight', {
  bind(el, binding) {
    el.style.background = binding.value
  },

  inserted(el) {},

  componentUpdated(el) {},

  unbind(el) {}
})
Afterjavascript
app.directive('highlight', {
  beforeMount(el, binding) {
    el.style.background = binding.value
  },

  mounted(el) {},

  updated(el) {},

  unmounted(el) {}
})
Migration

Rename directive lifecycle hooks to the Vue 3 lifecycle equivalents. Replace bind with beforeMount, inserted with mounted, componentUpdated with updated, and unbind with unmounted.

Destroy lifecycle hooks were renamed

Action required
breakingmedium impact

Vue 3 renames beforeDestroy to beforeUnmount and destroyed to unmounted.

Beforejavascript
export default {
  beforeDestroy() {
    // cleanup
  },

  destroyed() {
    // destroyed
  }
}
Afterjavascript
export default {
  beforeUnmount() {
    // cleanup
  },

  unmounted() {
    // unmounted
  }
}
Migration

Rename beforeDestroy to beforeUnmount and destroyed to unmounted.

data must be declared as a function

Action required
breakingmedium impact

Vue 3 requires the data option to be a function for component definitions.

Beforejavascript
const Component = {
  data: {
    count: 0
  }
}
Afterjavascript
const Component = {
  data() {
    return {
      count: 0
    }
  }
}
Migration

Convert object-based data options to functions that return the component state.

Mixin data is now merged shallowly

Action required
breakingmedium impact

Vue 3 changes data merging between components, mixins and extensions to a shallow merge instead of recursively merging nested objects.

Migration

Review components that rely on nested data objects being recursively merged from mixins or extends. Explicitly combine nested state where required.

Array watchers no longer trigger on mutation by default

Action required
breakinghigh impact

In Vue 3, watching an array triggers when the array is replaced. Watching mutations requires the deep option.

Beforejavascript
watch: {
  items() {
    this.handleItemsChanged()
  }
}
Afterjavascript
watch: {
  items: {
    handler() {
      this.handleItemsChanged()
    },
    deep: true
  }
}
Migration

Add the deep option to array watchers when the application needs to react to array mutations rather than only array replacement.

Transition enter and leave classes were renamed

Action required
breakingmedium impact

Vue 3 renames the initial transition classes to make the transition state names consistent.

Beforecss
.v-enter {
  opacity: 0;
}

.v-leave {
  opacity: 1;
}
Aftercss
.v-enter-from {
  opacity: 0;
}

.v-leave-from {
  opacity: 1;
}
Migration

Replace *-enter with *-enter-from and *-leave with *-leave-from. Update the related enter-class and leave-class component props as well.

TransitionGroup no longer renders a wrapper by default

Action required
breakingmedium impact

Vue 3 changes TransitionGroup so it does not render a wrapper element by default.

Migration

If the Vue 2 wrapper element is required for layout or styling, explicitly provide the tag prop on TransitionGroup.

Numeric keyCode modifiers were removed

Action required
breakingmedium impact

Vue 3 removes numeric keyCode modifiers and the config.keyCodes API.

Beforevue
<input @keyup.13="submit" />
Aftervue
<input @keyup.enter="submit" />
Migration

Replace numeric keyCode modifiers with named key modifiers. Remove custom config.keyCodes mappings and use KeyboardEvent key names or explicit event handling where necessary.

$on, $off and $once were removed

Action required
breakinghigh impact

Vue 3 removes the component instance event-emitter methods $on, $off and $once.

Migration

Replace Vue instances used as event buses with an external event emitter or another explicit state/event communication pattern.

$children was removed

Action required
breakingmedium impact

Vue 3 removes the $children instance property.

Migration

Replace code that depends on $children with explicit template refs, props, provide/inject, or another explicit parent-child communication mechanism.

propsData option was removed

Action required
breakingmedium impact

Vue 3 removes propsData when creating component instances. Root props can instead be passed as the second argument to createApp.

Beforejavascript
new Comp({
  propsData: {
    username: 'Evan'
  }
})
Afterjavascript
createApp(
  Comp,
  {
    username: 'Evan'
  }
)
Migration

Remove propsData usage. Pass root component props as the second argument to createApp or use normal component rendering APIs for child components.

Vue.set, Vue.delete, $set and $delete were removed

Action required
breakingmedium impact

Vue 3 proxy-based reactivity no longer requires special APIs for adding or deleting reactive object properties.

Beforejavascript
this.$set(user, 'name', 'Volkan')
this.$delete(user, 'legacy')
Afterjavascript
user.name = 'Volkan'
delete user.legacy
Migration

Replace Vue.set/$set with normal property assignment and Vue.delete/$delete with the JavaScript delete operator.

Mounted applications no longer replace the mount element

Action required
breakingmedium impact

Vue 3 mounts the rendered application inside the target container instead of replacing the target element itself.

Beforehtml
<div id="app"></div>

<!-- Vue 2 may replace #app with the rendered root -->
Afterhtml
<div id="app">
  <!-- Vue 3 renders the application here -->
</div>
Migration

Review CSS, DOM selectors and integrations that assume the mount container itself is replaced by the root component.

Custom element detection and is usage changed

Action required
breakingmedium impact

Vue 3 performs custom element checks during template compilation. The special is attribute is restricted to the reserved <component> element for component switching.

Migration

Move custom element detection to compilerOptions.isCustomElement in the compiler or build configuration. Review usages of is on native elements and use <component :is="..."> for dynamic Vue components.

Prop default factories no longer have access to this

Action required
breakingmedium impact

Vue 3 removes component instance access through this inside prop default factory functions. Raw incoming props are provided as an argument instead.

Beforejavascript
export default {
  props: {
    theme: {
      default() {
        return this.defaultTheme
      }
    }
  }
}
Afterjavascript
import { inject } from 'vue'

export default {
  props: {
    theme: {
      default(props) {
        return inject(
          'theme',
          'default-theme'
        )
      }
    }
  }
}
Migration

Remove component instance access from prop default factories. Use the raw props argument when another incoming prop is needed, or inject() when injected values are required.

Attribute coercion behavior changed

Action required
breakingmedium impact

Vue 3 removes the special enumerated-attribute coercion behavior. For non-boolean attributes, false is serialized as "false" instead of removing the attribute.

Beforevue
<div :aria-hidden="false"></div>

<!-- Vue 2 could remove some
     non-boolean attributes
     when bound to false -->
Aftervue
<div :aria-hidden="false"></div>

<!-- Vue 3 renders:
     aria-hidden="false"

     Use null or undefined
     to remove the attribute. -->
Migration

Review bindings that depend on false removing a non-boolean attribute. Use null or undefined when the attribute should be removed.

Plain template elements are now rendered natively

Action required
breakinglow impact

A <template> element without a Vue special directive is treated as a native template element in Vue 3 instead of transparently rendering only its inner content.

Migration

Review plain <template> elements that do not use v-if, v-else, v-for or v-slot. Remove unnecessary template wrappers when the Vue 2 transparent-wrapper behavior was intended.

VNode lifecycle event prefix changed

Action required
breakingmedium impact

Vue 3 replaces the hook: prefix used for VNode lifecycle events with vue:. These events can also be used on HTML elements.

Beforevue
<ChildComponent
  @hook:updated="onUpdated"
/>
Aftervue
<ChildComponent
  @vue:updated="onUpdated"
/>
Migration

Replace hook: lifecycle event prefixes with vue:. Also account for the beforeDestroy/beforeUnmount and destroyed/unmounted lifecycle renames where applicable.

inline-template was removed

Action required
breakingmedium impact

Vue 3 removes the inline-template attribute that allowed child component content to act as the component template.

Beforevue
<MyComponent inline-template>
  <div>
    {{ message }}
  </div>
</MyComponent>
Aftervue
<MyComponent v-slot="{ message }">
  <div>
    {{ message }}
  </div>
</MyComponent>
Migration

Replace inline-template usage with regular component templates, external template sources, or scoped/default slots depending on the original use case.

$destroy was removed

Action required
breakingmedium impact

Vue 3 removes the $destroy component instance method. Applications should not manually manage the lifecycle of individual component instances.

Migration

Remove manual $destroy calls and control component lifetime through normal declarative rendering and application unmounting.

Global APIs are now tree-shakeable named exports

Action required
breakinghigh impact

Vue 3 restructures a number of global APIs as named ES module exports so unused APIs can be removed by tree-shaking.

Beforejavascript
import Vue from 'vue'

Vue.nextTick(() => {
  // DOM update complete
})

const state = Vue.observable({
  count: 0
})
Afterjavascript
import {
  nextTick,
  reactive
} from 'vue'

nextTick(() => {
  // DOM update complete
})

const state = reactive({
  count: 0
})
Migration

Replace affected Vue.* global API calls with named imports from vue. For example, import nextTick directly and replace Vue.observable with reactive.

New Features

Components can have multiple root nodes

featuremedium impact

Vue 3 introduces fragment support, allowing components to render multiple root nodes instead of requiring a single root element.

Beforevue
<template>
  <div>
    <header>...</header>
    <main>...</main>
  </div>
</template>
Aftervue
<template>
  <header>...</header>
  <main>...</main>
</template>
Migration

The wrapper element can be removed when it only existed to satisfy the Vue 2 single-root requirement. For multi-root components, review attribute inheritance because Vue cannot automatically determine which root should receive fallthrough attributes.

Components can declare emitted events

featuremedium impact

Vue 3 introduces the emits option, allowing components to explicitly declare the events they emit, similar to how props declares accepted properties.

Beforevue
<script>
export default {
  props: ['text'],

  methods: {
    accept() {
      this.$emit('accepted')
    }
  }
}
</script>
Aftervue
<script>
export default {
  props: ['text'],

  emits: ['accepted'],

  methods: {
    accept() {
      this.$emit('accepted')
    }
  }
}
</script>
Migration

Declare the events emitted by each component using the emits option. This is especially important when migrating components that previously relied on the .native modifier or re-emitted native DOM events.

Composition API introduced

featurehigh impact

Vue 3 introduces the Composition API, providing APIs such as setup(), ref(), reactive(), computed() and lifecycle hooks for organizing component logic by feature instead of option type.

Beforevue
<script>
export default {
  data() {
    return {
      count: 0
    }
  },

  computed: {
    doubled() {
      return this.count * 2
    }
  },

  methods: {
    increment() {
      this.count++
    }
  }
}
</script>
Aftervue
<script>
import {
  ref,
  computed
} from 'vue'

export default {
  setup() {
    const count = ref(0)

    const doubled = computed(
      () => count.value * 2
    )

    const increment = () => {
      count.value++
    }

    return {
      count,
      doubled,
      increment
    }
  }
}
</script>
Migration

Existing Options API components do not need to be rewritten. Composition API is an additional way to author components and can be adopted incrementally where it improves logic reuse or organization.

Teleport component introduced

featuremedium impact

Vue 3 introduces the built-in <Teleport> component, allowing part of a component template to be rendered into a DOM node outside of the component’s own DOM hierarchy.

Beforevue
<template>
  <div class="page">
    <MyModal v-if="showModal" />
  </div>
</template>
Aftervue
<template>
  <div class="page">
    <!-- page content -->
  </div>

  <Teleport to="body">
    <MyModal v-if="showModal" />
  </Teleport>
</template>
Migration

No migration is required. Teleport can replace custom portal-style solutions when UI such as modals, overlays or notifications needs to render outside the component DOM hierarchy.

Suspense component introduced

featuremedium impact Experimental

Vue 3 introduces the built-in <Suspense> component for coordinating asynchronous dependencies in a component tree and displaying fallback content while they resolve.

Beforevue
<template>
  <div v-if="loading">
    Loading...
  </div>

  <AsyncComponent v-else />
</template>
Aftervue
<template>
  <Suspense>
    <template #default>
      <AsyncComponent />
    </template>

    <template #fallback>
      Loading...
    </template>
  </Suspense>
</template>
Migration

No migration is required. Suspense can coordinate loading states for supported asynchronous dependencies, but it should be adopted with care because the API is experimental.

Custom Renderer API introduced

featurelow impact

Vue 3 exposes a custom renderer API that allows Vue components and its reactivity system to target rendering environments other than the browser DOM.

Migration

No migration is required for normal Vue applications. The API is intended primarily for authors building custom renderers for non-DOM environments.

Component state can be used directly in SFC CSS

featurelow impact

Vue 3 supports the v-bind() CSS function in Single-File Component style blocks, allowing CSS values to react to component state.

Beforevue
<template>
  <div :style="{ color: themeColor }">
    Hello
  </div>
</template>

<script>
export default {
  data() {
    return {
      themeColor: 'red'
    }
  }
}
</script>
Aftervue
<template>
  <div class="message">
    Hello
  </div>
</template>

<script>
export default {
  data() {
    return {
      themeColor: 'red'
    }
  }
}
</script>

<style>
.message {
  color: v-bind(themeColor);
}
</style>
Migration

No migration is required. CSS v-bind() can be adopted when reactive component state should be consumed directly inside an SFC style block.

onServerPrefetch added to the Composition API

featurelow impact

Vue 3.1 adds onServerPrefetch(), allowing Composition API components to register asynchronous work that should be resolved during server-side rendering.

Aftervue
<script>
import {
  ref,
  onServerPrefetch
} from 'vue'

export default {
  setup() {
    const data = ref(null)

    onServerPrefetch(async () => {
      data.value = await fetchData()
    })

    return {
      data
    }
  }
}
</script>
Migration

No migration is required. Use onServerPrefetch() when Composition API logic needs to fetch or prepare asynchronous data during server-side rendering.

Official sources

<script setup> became stable

featurehigh impact

Vue 3.2 stabilizes <script setup>, a compile-time syntax for using the Composition API inside Single-File Components with less boilerplate.

Beforevue
<script>
import { ref } from "vue"

export default {
  setup() {
    const count = ref(0)

    const increment = () => {
      count.value++
    }

    return {
      count,
      increment
    }
  }
}
</script>
Aftervue
<script setup>
import { ref } from "vue"

const count = ref(0)

const increment = () => {
  count.value++
}
</script>
Migration

No migration is required. Existing setup() components remain valid. <script setup> can be adopted incrementally to reduce Composition API boilerplate in Single-File Components.

:slotted() selector became stable

featurelow impact

Vue 3.2 stabilizes the :slotted() pseudo-class for scoped SFC styles, allowing a component to explicitly style content passed through slots.

Aftervue
<template>
  <slot />
</template>

<style scoped>
:slotted(.message) {
  color: red;
}
</style>
Migration

No migration is required. Use :slotted() when scoped component styles need to target elements provided by a parent through slots.

Web Components support with defineCustomElement

featuremedium impact

Vue 3.2 introduces defineCustomElement(), allowing Vue components to be packaged and registered as native Custom Elements.

Aftervue
import {
  defineCustomElement
} from "vue"
import MyElement from "./MyElement.ce.vue"

const MyCustomElement =
  defineCustomElement(MyElement)

customElements.define(
  "my-element",
  MyCustomElement
)
Migration

No migration is required. Use defineCustomElement() when Vue components need to be distributed or consumed as native Web Components.

Effect scopes introduced

featuremedium impact

Vue 3.2 introduces effectScope() for grouping reactive effects such as computed values and watchers so they can be disposed together.

Aftervue
import {
  effectScope,
  computed,
  watch
} from "vue"

const scope = effectScope()

scope.run(() => {
  const doubled = computed(
    () => state.count * 2
  )

  watch(
    () => state.count,
    () => {
      // react to changes
    }
  )
})

scope.stop()
Migration

No migration is required. effectScope() is primarily useful for reusable composables and libraries that need explicit control over groups of reactive effects.

Web Streams SSR API introduced

featurelow impact

Vue 3.2 expands server-side rendering APIs with renderToWebStream() for environments that support the Web Streams API.

Aftervue
import {
  renderToWebStream
} from "@vue/server-renderer"

const stream =
  renderToWebStream(app)
Migration

No migration is required. The streaming API can be adopted by SSR runtimes that support Web Streams.

Performance

Reactivity performance improved

performancemedium impact

Vue 3.2 includes major reactivity performance improvements, reducing overhead in dependency tracking and reactive effect execution.

Migration

No migration is required. Existing applications receive the reactivity performance improvements after upgrading.

Template compiler performance improved

performancelow impact

Vue 3.2 improves template compiler performance, reducing compilation costs for templates and Single-File Components.

Migration

No migration is required. Applications and build tooling benefit automatically after upgrading.

Runtime memory usage reduced

performancelow impact

Vue 3.2 includes runtime optimizations that reduce memory usage and improve performance when creating and updating component trees.

Migration

No migration is required. The runtime optimizations apply automatically after upgrading.

Tooling

SFC compiler source map support improved

toolinglow impact

Vue 3.1 includes compiler improvements for generating and propagating source maps for Single-File Components.

Migration

No migration is required. Tooling built on @vue/compiler-sfc can benefit from the improved source map support.

Official sources

SSR package became ESM-bundler compatible

toolinglow impact

Vue 3.2 improves server-side rendering support, including an ESM-bundler build of @vue/server-renderer for better integration with modern build tools.

Migration

No migration is required for most applications. SSR tooling and frameworks can use the improved server-renderer packaging with modern ESM-based build pipelines.

Migration Tools

Migration Build for Vue 2 applications

migrationmedium impact

Vue 3.1 introduced the @vue/compat migration build, which provides Vue 2 compatible behavior with runtime warnings for APIs and behaviors that need to be migrated.

Migration

Projects migrating from Vue 2 can temporarily alias vue to @vue/compat and use compatibility warnings to migrate incrementally. The compatibility build is intended as a migration aid and should be removed after migration is complete.