Vue.js Upgrade Guide

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

33Changes
6Breaking
6Actions
13Features
0Migration tools

Breaking Changes

defineComponent() function overload type changed

Action required
breakinglow impact

Vue 3.3 repurposes the rarely used defineComponent() function overload to support improved generic component typing, changing its TypeScript type behavior.

Migration

Review code that passes a function directly to defineComponent(). This overload was rarely used, but its type signature changed in Vue 3.3.

Official sources

Global JSX namespace registration was removed

Action required
breakinghigh impact

Vue 3.4 no longer registers the global JSX namespace by default, avoiding global type collisions with other JSX ecosystems such as React.

Beforevue
{
  "compilerOptions": {
    "jsx": "preserve"
  }
}
Aftervue
{
  "compilerOptions": {
    "jsx": "preserve",
    "jsxImportSource": "vue"
  }
}
Migration

TSX users should set jsxImportSource to vue in tsconfig.json or add an @jsxImportSource vue pragma per file. Projects that depend on the old global JSX namespace can explicitly reference vue/jsx.

Reactivity Transform was removed from Vue core

Action required
breakinghigh impact

Vue 3.4 removes the experimental Reactivity Transform feature, including compile-time macros such as $ref(), from Vue core.

Beforevue
<script setup>
let count = $ref(0)

count++
</script>
Aftervue
<script setup>
import { ref } from "vue"

const count = ref(0)

count.value++
</script>
Migration

Replace Reactivity Transform macros with standard Vue reactivity APIs, or migrate to the Vue Macros plugin if the transform syntax should be retained.

app.config.unwrapInjectedRef was removed

Action required
breakinglow impact

Vue 3.4 removes app.config.unwrapInjectedRef. Injected refs are unwrapped according to the behavior that had already become the default in Vue 3.3.

Beforevue
app.config.unwrapInjectedRef = false
Migration

Remove usages of app.config.unwrapInjectedRef. Vue 3.4 no longer allows opting out of the injected-ref unwrapping behavior.

Official sources

Deprecated @vnodeXXX event listeners became compiler errors

Action required
breakingmedium impact

Vue 3.4 removes the deprecated @vnodeXXX template event syntax. The replacement @vue:XXX lifecycle listener syntax must be used.

Beforevue
<MyComponent
  @vnodeMounted="onMounted"
/>
Aftervue
<MyComponent
  @vue:mounted="onMounted"
/>
Migration

Replace deprecated @vnodeXXX lifecycle event listeners with the corresponding @vue:XXX syntax.

Official sources

Deprecated v-is directive was removed

Action required
breakingmedium impact

Vue 3.4 removes the deprecated v-is directive.

Beforevue
<tr v-is="'vue:my-row'"></tr>
Aftervue
<tr is="vue:my-row"></tr>
Migration

Replace v-is with the is attribute and the vue: prefix where Vue component resolution on native elements is required.

Official sources

New Features

<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.

defineOptions() introduced

featuremedium impact

Vue 3.3 introduces defineOptions(), allowing additional component options such as inheritAttrs to be declared directly inside <script setup>.

Beforevue
<script>
export default {
  inheritAttrs: false
}
</script>

<script setup>
// component logic
</script>
Aftervue
<script setup>
defineOptions({
  inheritAttrs: false
})
</script>
Migration

No migration is required. Components that previously needed a second normal <script> block solely for supported component options can use defineOptions().

Reactive Props Destructure introduced experimentally

featuremedium impact Experimental

Vue 3.3 introduces experimental support for destructuring defineProps() while preserving reactivity and allowing native default-value syntax.

Migration

No migration is required. This feature was experimental in Vue 3.3 and should be treated according to the behavior and stability of the target Vue version.

defineModel() introduced experimentally

featuremedium impact Experimental

Vue 3.3 introduces the experimental defineModel() compiler macro for declaring a component v-model prop and its corresponding update event with less boilerplate.

Beforevue
<script setup>
const props = defineProps({
  modelValue: String
})

const emit = defineEmits([
  "update:modelValue"
])
</script>
Aftervue
<script setup>
const modelValue =
  defineModel<string>()
</script>
Migration

No migration is required. In Vue 3.3 defineModel() was experimental, so adoption should account for the stability of the target Vue release.

toRef() normalization improved and toValue() introduced

featuremedium impact

Vue 3.3 enhances toRef() so it can normalize values, getters and existing refs, and introduces toValue() for normalizing values, refs and getters into values.

Aftervue
import {
  ref,
  toRef,
  toValue
} from "vue"

const existing = ref(1)

toRef(1)
toRef(() => props.count)
toRef(existing)

toValue(1)
toValue(existing)
toValue(() => props.count)
Migration

No migration is required. Composables can use toValue() when accepting values, refs or getters, and the normalization form of toRef() when a ref representation is needed.

Reactivity APIs gained better getter support

featuremedium impact

Vue 3.3 improves getter handling in reactivity utilities, making getter-based reactive inputs easier and more efficient to consume in composables.

Beforevue
useFeature(
  computed(() => props.id)
)
Aftervue
useFeature(
  () => props.id
)
Migration

No migration is required. Composables can increasingly accept getters directly instead of requiring callers to allocate intermediate computed refs.

defineModel() became stable

featurehigh impact

Vue 3.4 promotes defineModel() from experimental to stable, providing a concise way to declare a component v-model prop and its corresponding update event.

Beforevue
<script setup lang="ts">
const props = defineProps<{
  modelValue: string
}>()

const emit = defineEmits<{
  "update:modelValue": [value: string]
}>()
</script>
Aftervue
<script setup lang="ts">
const model = defineModel<string>()
</script>
Migration

No migration is required. Projects that avoided defineModel() while it was experimental can now adopt the stable API. Vue 3.4 also removes the previous local option and supports local mutation by default.

Same-name v-bind shorthand introduced

featurelow impact

Vue 3.4 supports a shorthand for bindings where the attribute name and JavaScript variable name are identical.

Beforevue
<img
  :id="id"
  :src="src"
  :alt="alt"
/>
Aftervue
<img
  :id
  :src
  :alt
/>
Migration

No migration is required. The shorthand can be adopted where a v-bind argument has the same name as the bound variable.

Official sources

MathML support introduced

featurelow impact

Vue 3.4 adds built-in recognition and rendering support for MathML elements.

Aftervue
<template>
  <math>
    <mfrac>
      <mi>a</mi>
      <mi>b</mi>
    </mfrac>
  </math>
</template>
Migration

No migration is required. MathML can be used directly in Vue templates.

Official sources

TypeScript

SFC macros support imported and complex TypeScript types

typescripthigh impact

Vue 3.3 expands type support in macros such as defineProps() and defineEmits(), allowing imported types and a broader set of complex TypeScript types to be used directly.

Beforevue
<script setup lang="ts">
interface Props {
  name: string
  age?: number
}

defineProps<Props>()
</script>
Aftervue
<script setup lang="ts">
import type { UserProps } from "./types"

defineProps<UserProps>()
</script>
Migration

No migration is required. Existing local type declarations remain valid. Imported and supported complex types can now be used directly with SFC compiler macros.

Generic components supported in <script setup>

typescripthigh impact

Vue 3.3 adds native support for declaring generic Single-File Components using the generic attribute on <script setup>.

Aftervue
<script
  setup
  lang="ts"
  generic="T extends string | number"
>
defineProps<{
  items: T[]
  selected: T
}>()
</script>
Migration

No migration is required. Generic components can now express relationships between prop types directly in <script setup>.

defineEmits gained a more ergonomic TypeScript syntax

typescriptmedium impact

Vue 3.3 allows type-based defineEmits declarations to use event names as object keys and tuple types for event arguments.

Beforevue
const emit = defineEmits<{
  (e: "change", id: number): void
  (e: "update", value: string): void
}>()
Aftervue
const emit = defineEmits<{
  change: [id: number]
  update: [value: string]
}>()
Migration

No migration is required. The previous call-signature syntax remains supported. The tuple syntax can be adopted for more concise event typing.

Typed slots with defineSlots()

typescripthigh impact

Vue 3.3 introduces the defineSlots() compiler macro for providing IDE and vue-tsc type information about slot names and slot props.

Aftervue
<script setup lang="ts">
defineSlots<{
  default?: (
    props: { message: string }
  ) => any

  item?: (
    props: { id: number }
  ) => any
}>()
</script>
Migration

No migration is required. defineSlots() can be adopted when slot names and slot props should be type checked.

JSX import source support added

typescriptmedium impact

Vue 3.3 adds support for TypeScript's jsxImportSource option, allowing Vue JSX typing to be opted into explicitly and reducing conflicts with other JSX ecosystems.

Aftervue
{
  "compilerOptions": {
    "jsx": "preserve",
    "jsxImportSource": "vue"
  }
}
Migration

Vue 3.3 still provides the global JSX namespace for backwards compatibility, but TSX users should configure jsxImportSource for forward compatibility because the default global registration was planned for removal in Vue 3.4.

defineComponent() gained generic component support

typescriptmedium impact

Vue 3.3 improves TypeScript support for authoring generic components with defineComponent(), complementing generic support in <script setup>.

Migration

No migration is required. Library and component authors can use the improved generic typing when component APIs need to preserve relationships between types.

Official sources

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.

Template parser rewritten for substantially better performance

performancemedium impact

Vue 3.4 introduces a rewritten template parser that is approximately twice as fast, improving Single-File Component and template compilation performance.

Migration

No migration is required. Applications and build tooling receive the parser performance improvements after upgrading.

Official sources

Reactivity system became more efficient

performancehigh impact

Vue 3.4 refactors the reactivity system so computed values and effects trigger more accurately and avoid unnecessary executions in a number of common cases.

Migration

No migration is required. Existing applications benefit automatically from the more efficient reactivity implementation.

Official sources

Tooling

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.

SSR hydration mismatch diagnostics improved

toolingmedium impact

Vue 3.4 improves hydration mismatch checks and development diagnostics, providing clearer information about mismatched server and client output.

Migration

No migration is required. SSR applications receive more informative hydration mismatch diagnostics after upgrading.

Production hydration mismatch details can be enabled

toolinglow impact

Vue 3.4 adds the false compile-time feature flag for including detailed hydration mismatch information in production builds.

Migration

No migration is required. SSR tooling can opt into detailed production hydration mismatch diagnostics when the additional information is useful.

Official sources