Vue.js Upgrade Guide

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

28Changes
6Breaking
6Actions
16Features
0Migration tools

Breaking Changes

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

Deprecated parseExpressions option was removed

Action required
breakinglow impact

Vue 3.5 removes the previously deprecated parseExpressions compiler option.

Migration

Remove usage of the deprecated parseExpressions compiler option from custom compiler integrations.

Official sources

New Features

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

Reactive Props Destructure became stable

featurehigh impact

Vue 3.5 enables Reactive Props Destructure by default. Variables destructured from defineProps() remain reactive and can use native JavaScript default-value syntax.

Beforevue
<script setup lang="ts">
const props = withDefaults(
  defineProps<{
    count?: number
  }>(),
  {
    count: 0
  }
)

watchEffect(() => {
  console.log(props.count)
})
</script>
Aftervue
<script setup lang="ts">
const {
  count = 0
} = defineProps<{
  count?: number
}>()

watchEffect(() => {
  console.log(count)
})
</script>
Migration

No migration is required. Reactive Props Destructure is enabled by default in Vue 3.5 and can be adopted incrementally.

useTemplateRef() introduced

featurehigh impact

Vue 3.5 introduces useTemplateRef(), providing a cleaner Composition API for accessing template refs without manually declaring matching ref variables.

Beforevue
<script setup>
import {
  ref,
  onMounted
} from "vue"

const input = ref(null)

onMounted(() => {
  input.value.focus()
})
</script>

<template>
  <input ref="input" />
</template>
Aftervue
<script setup>
import {
  useTemplateRef,
  onMounted
} from "vue"

const input =
  useTemplateRef("input")

onMounted(() => {
  input.value?.focus()
})
</script>

<template>
  <input ref="input" />
</template>
Migration

No migration is required. Existing template refs remain valid. useTemplateRef() can be adopted for clearer template-ref access and improved tooling support.

onWatcherCleanup() introduced

featurehigh impact

Vue 3.5 introduces onWatcherCleanup(), allowing cleanup logic to be registered from inside watch and watchEffect callbacks.

Aftervue
import {
  watch,
  onWatcherCleanup
} from "vue"

watch(id, async (newId) => {
  const controller =
    new AbortController()

  onWatcherCleanup(() => {
    controller.abort()
  })

  await fetch(
    `/api/users/${newId}`,
    {
      signal: controller.signal
    }
  )
})
Migration

No migration is required. onWatcherCleanup() provides an alternative cleanup API for watchers and is particularly useful for cancelling stale asynchronous work.

Watchers gained pause and resume controls

featuremedium impact

Vue 3.5 extends watcher handles with pause() and resume(), allowing watcher execution to be temporarily suspended without stopping the watcher permanently.

Aftervue
const {
  stop,
  pause,
  resume
} = watch(source, callback)

pause()

// watcher temporarily suspended

resume()

// watcher active again

stop()
Migration

No migration is required. Existing watcher handles continue to work and can optionally use pause() and resume().

Official sources

Watcher deep option accepts a maximum traversal depth

featuremedium impact

Vue 3.5 allows the deep option on watchers to be a number, limiting how many object levels Vue traverses when tracking nested changes.

Beforevue
watch(
  state,
  callback,
  {
    deep: true
  }
)
Aftervue
watch(
  state,
  callback,
  {
    deep: 2
  }
)
Migration

No migration is required. Numeric deep values can be used to reduce traversal costs when full deep observation is unnecessary.

Official sources

Lazy hydration strategies introduced

featurehigh impact

Vue 3.5 allows async components to control when server-rendered markup is hydrated using built-in strategies such as idle, visibility, interaction and media-query hydration.

Aftervue
import {
  defineAsyncComponent,
  hydrateOnVisible
} from "vue"

const AsyncCard =
  defineAsyncComponent({
    loader: () =>
      import("./Card.vue"),

    hydrate:
      hydrateOnVisible()
  })
Migration

No migration is required. SSR applications can adopt lazy hydration strategies for async components where immediate hydration is unnecessary.

useId() introduced

featuremedium impact

Vue 3.5 introduces useId() for generating application-unique IDs that remain stable across server rendering and client hydration.

Aftervue
<script setup>
import { useId } from "vue"

const id = useId()
</script>

<template>
  <label :for="id">
    Name
  </label>

  <input :id="id" />
</template>
Migration

No migration is required. useId() is useful for accessible form relationships and reusable SSR-safe components that require unique identifiers.

Teleport can be deferred

featuremedium impact

Vue 3.5 adds the defer prop to Teleport, allowing its target to be resolved after other parts of the same update cycle have mounted.

Beforevue
<Teleport to="#target">
  <Modal />
</Teleport>

<!-- target generally needs
     to already exist -->
Aftervue
<Teleport
  defer
  to="#target"
>
  <Modal />
</Teleport>

<div id="target"></div>
Migration

No migration is required. Use defer when the Teleport target is rendered later in the same component tree.

Teleport can be nested directly inside Transition

featurelow impact

Vue 3.5 supports directly nesting Teleport inside Transition, simplifying transition handling for teleported content.

Migration

No migration is required. Existing Teleport and Transition usage remains valid.

Official sources

Hydration mismatches can be selectively suppressed

featuremedium impact

Vue 3.5 introduces the data-allow-mismatch attribute for intentionally suppressing specific SSR hydration mismatch warnings.

Aftervue
<span
  data-allow-mismatch="text"
>
  {{ new Date().toLocaleString() }}
</span>
Migration

No migration is required. Use data-allow-mismatch only for intentional server/client differences rather than hiding actual hydration bugs.

Applications can register unmount cleanup callbacks

featurelow impact

Vue 3.5 introduces app.onUnmount(), allowing plugins and application-level integrations to register cleanup callbacks that run when the application is unmounted.

Aftervue
const app = createApp(App)

app.onUnmount(() => {
  cleanupExternalResources()
})

app.mount("#app")
Migration

No migration is required. Plugin and integration authors can use app.onUnmount() for application-level cleanup.

Official sources

Custom Element support significantly improved

featuremedium impact

Vue 3.5 improves Vue Custom Elements with APIs and behavior for host access, shadow-root configuration, CSP nonces and other integration scenarios.

Migration

No migration is required. Applications and libraries using Vue Custom Elements can adopt the expanded configuration and host APIs as needed.

Unhandled production errors can be configured to throw

featuremedium impact

Vue 3.5 adds app.config.throwUnhandledErrorInProduction for applications that want unhandled framework errors to be thrown in production instead of only being logged.

Aftervue
const app = createApp(App)

app.config
  .throwUnhandledErrorInProduction =
  true
Migration

No migration is required. Enable this option only when production error handling and monitoring are prepared for thrown unhandled errors.

Official sources

Performance

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

Reactivity system was significantly optimized

performancehigh impact

Vue 3.5 refactors the reactivity system using version counting and doubly-linked dependency tracking, improving performance and reducing memory usage without changing expected application behavior.

Migration

No migration is required. Existing applications automatically benefit after upgrading.

Reactive array tracking was optimized

performancemedium impact

Vue 3.5 improves reactive array tracking, significantly reducing overhead for large reactive arrays and common array operations.

Migration

No migration is required. Applications using large reactive arrays receive the optimization automatically.

Official sources

Tooling

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