Breaking Changes
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.
Vue 3.4 no longer registers the global JSX namespace by default, avoiding global type collisions with other JSX ecosystems such as React.
{
"compilerOptions": {
"jsx": "preserve"
}
}
{
"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.
Vue 3.4 removes the experimental Reactivity Transform feature, including compile-time macros such as $ref(), from Vue core.
<script setup>
let count = $ref(0)
count++
</script>
<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.
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.
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.
Vue 3.4 removes the deprecated @vnodeXXX template event syntax. The replacement @vue:XXX lifecycle listener syntax must be used.
<MyComponent
@vnodeMounted="onMounted"
/>
<MyComponent
@vue:mounted="onMounted"
/>
Migration Replace deprecated @vnodeXXX lifecycle event listeners with the corresponding @vue:XXX syntax.
Vue 3.4 removes the deprecated v-is directive.
<tr v-is="'vue:my-row'"></tr>
<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.
Vue 3.5 removes the previously deprecated parseExpressions compiler option.
Migration Remove usage of the deprecated parseExpressions compiler option from custom compiler integrations.
New Features
Vue 3.1 adds onServerPrefetch(), allowing Composition API components to register asynchronous work that should be resolved during server-side rendering.
<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.
Vue 3.2 stabilizes <script setup>, a compile-time syntax for using the Composition API inside Single-File Components with less boilerplate.
<script>
import { ref } from "vue"
export default {
setup() {
const count = ref(0)
const increment = () => {
count.value++
}
return {
count,
increment
}
}
}
</script>
<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.
Vue 3.2 stabilizes the :slotted() pseudo-class for scoped SFC styles, allowing a component to explicitly style content passed through slots.
<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.
Vue 3.2 introduces defineCustomElement(), allowing Vue components to be packaged and registered as native Custom Elements.
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.
Vue 3.2 introduces effectScope() for grouping reactive effects such as computed values and watchers so they can be disposed together.
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.
Vue 3.2 expands server-side rendering APIs with renderToWebStream() for environments that support the Web Streams API.
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.
Vue 3.3 introduces defineOptions(), allowing additional component options such as inheritAttrs to be declared directly inside <script setup>.
<script>
export default {
inheritAttrs: false
}
</script>
<script setup>
// component logic
</script>
<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().
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.
Vue 3.3 introduces the experimental defineModel() compiler macro for declaring a component v-model prop and its corresponding update event with less boilerplate.
<script setup>
const props = defineProps({
modelValue: String
})
const emit = defineEmits([
"update:modelValue"
])
</script>
<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.
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.
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.
Vue 3.3 improves getter handling in reactivity utilities, making getter-based reactive inputs easier and more efficient to consume in composables.
useFeature(
computed(() => props.id)
)
useFeature(
() => props.id
)
Migration No migration is required. Composables can increasingly accept getters directly instead of requiring callers to allocate intermediate computed refs.
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.
<script setup lang="ts">
const props = defineProps<{
modelValue: string
}>()
const emit = defineEmits<{
"update:modelValue": [value: string]
}>()
</script>
<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.
Vue 3.4 supports a shorthand for bindings where the attribute name and JavaScript variable name are identical.
<img
:id="id"
:src="src"
:alt="alt"
/>
Migration No migration is required. The shorthand can be adopted where a v-bind argument has the same name as the bound variable.
Vue 3.4 adds built-in recognition and rendering support for MathML elements.
<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.
Vue 3.5 enables Reactive Props Destructure by default. Variables destructured from defineProps() remain reactive and can use native JavaScript default-value syntax.
<script setup lang="ts">
const props = withDefaults(
defineProps<{
count?: number
}>(),
{
count: 0
}
)
watchEffect(() => {
console.log(props.count)
})
</script>
<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.
Vue 3.5 introduces useTemplateRef(), providing a cleaner Composition API for accessing template refs without manually declaring matching ref variables.
<script setup>
import {
ref,
onMounted
} from "vue"
const input = ref(null)
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="input" />
</template>
<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.
Vue 3.5 introduces onWatcherCleanup(), allowing cleanup logic to be registered from inside watch and watchEffect callbacks.
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.
Vue 3.5 extends watcher handles with pause() and resume(), allowing watcher execution to be temporarily suspended without stopping the watcher permanently.
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().
Vue 3.5 allows the deep option on watchers to be a number, limiting how many object levels Vue traverses when tracking nested changes.
watch(
state,
callback,
{
deep: true
}
)
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.
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.
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.
Vue 3.5 introduces useId() for generating application-unique IDs that remain stable across server rendering and client hydration.
<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.
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.
<Teleport to="#target">
<Modal />
</Teleport>
<!-- target generally needs
to already exist -->
<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.
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.
Vue 3.5 introduces the data-allow-mismatch attribute for intentionally suppressing specific SSR hydration mismatch warnings.
<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.
Vue 3.5 introduces app.onUnmount(), allowing plugins and application-level integrations to register cleanup callbacks that run when the application is unmounted.
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.
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.
Vue 3.5 adds app.config.throwUnhandledErrorInProduction for applications that want unhandled framework errors to be thrown in production instead of only being logged.
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.
TypeScript
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.
<script setup lang="ts">
interface Props {
name: string
age?: number
}
defineProps<Props>()
</script>
<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.
Vue 3.3 adds native support for declaring generic Single-File Components using the generic attribute on <script setup>.
<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>.
Vue 3.3 allows type-based defineEmits declarations to use event names as object keys and tuple types for event arguments.
const emit = defineEmits<{
(e: "change", id: number): void
(e: "update", value: string): void
}>()
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.
Vue 3.3 introduces the defineSlots() compiler macro for providing IDE and vue-tsc type information about slot names and slot props.
<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.
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.
{
"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.
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.
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.
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.
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.
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.
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.
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.
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.