The Complete Vue 3.6 RC Upgrade Guide—from 3.5 to Vapor Mode and alien-signals
As of Vue 3.6.0-rc.2, this guide compares Vue 3.5 and 3.6 across the reactivity core, Vapor Mode, event delegation, interoperability boundaries, and a progressive upgrade strategy.

If you look only at the version number, Vue 3.6 appears to be an ordinary minor release. After reading the release notes in full, however, my impression is different: it feels more like Vue is replacing its engine for the next stage.
On one side is a reactivity core rebuilt on alien-signals while the public ref, computed, and watch APIs remain largely unchanged. On the other is Vapor Mode entering the RC stage, allowing Vue single-file components to bypass the Virtual DOM and compile into more direct DOM updates.
First, the most important version detail:
As of July 26, 2026, the latest Vue 3.6 release is 3.6.0-rc.2. RC means release candidate, not stable. This article is intended for evaluation, experimentation, and migration planning; it does not mean every production project should upgrade immediately.
The short version: what changed in 3.6?
| Dimension | Vue 3.5 | Vue 3.6 RC | Practical effect on existing projects |
|---|---|---|---|
| Reactivity system | Internally optimized in 3.5 | Substantially rebuilt on alien-signals | Ordinary Composition API code usually needs no rewrite, but complex effects and scheduling deserve regression tests |
| Default rendering | Virtual DOM | Still Virtual DOM | Upgrading dependencies alone does not move an application into Vapor |
| Vapor Mode | Not delivered as a production capability in 3.5 | Feature set is in RC and 100% opt-in | Teams can experiment with one performance-sensitive area |
| Application size | Includes the VDOM runtime by default | A pure Vapor application can omit the VDOM runtime | Size savings are easiest to realize in pure Vapor applications or clearly separated regions |
| API coverage | Full Vue 3 API ecosystem | Vapor supports a subset of Vue APIs | Options API and instance-proxy features cannot be migrated directly |
| VDOM / Vapor mixing | Not applicable | Interoperability through vaporInteropPlugin | Standard props, events, and slots are covered, but complex nesting still needs testing |
| Event delegation | Native listener semantics | Direct binding by default since rc.2; .delegate opts in | Do not carry the rc.1 assumption of automatic delegation forward |
| Production readiness | Stable | Still a prerelease | Production projects should wait for stable Vue and explicit ecosystem support |
In one sentence:
Vue 3.5 focused more on developer experience and SSR capabilities, while Vue 3.6 focuses on the reactivity core and rendering architecture.
From 3.5 to 3.6: the center of gravity has moved
Vue 3.5 introduced no breaking changes, and most of its improvements were immediately visible in application code:
- Reactive Props Destructure became stable.
useTemplateRef()improved how template refs are organized.onWatcherCleanup()made watcher cleanup more natural.useId()provided stable, SSR-friendly IDs.- Async components gained Lazy Hydration.
data-allow-mismatchallowed declared hydration differences.- Deferred Teleport and custom elements were improved.
Vue 3.6 contains many fixes too, but its two most important changes—alien-signals and Vapor—sit beneath application code. You may not write a new API in your business logic, but you will encounter different performance characteristics, compiler output, and runtime boundaries.
That changes the upgrade strategy: do not stop at “Does it compile?” Test reactive behavior, SSR hydration, component interoperability, and real performance.
Change one: the reactivity core moves to alien-signals
Vue 3.6 substantially rewrites @vue/reactivity on a foundation derived from StackBlitz’s open-source alien-signals.
For most application developers, this is still familiar Vue:
import { computed, ref, watchEffect } from 'vue'
const price = ref(99)
const count = ref(2)
const total = computed(() => price.value * count.value)
watchEffect(() => {
console.log(total.value)
})
The changes are internal: how dependencies are established, how state is marked, when computed values become stale, how effects propagate through the dependency graph, and how those relationships are cleaned up and reused.

This illustration expresses the concept of dependency propagation; it is not a field-by-field diagram of Vue’s internal data structures.
How to interpret the performance numbers
The alien-signals merge PR in Vue’s core repository reported two kinds of microbenchmark:
- In a scenario constructing many
ref,computed, and effect instances, sample memory fell from roughly 2.3 MB to 2.0 MB, a reduction of about 13%. - In a particular stress case where many computed values were read after one source changed, performance could improve by more than 30 times.
The second number is striking, but it does not mean “a Vue 3.6 application is 30 times faster.” It demonstrates that the new dependency-propagation algorithm has better complexity characteristics for some graph structures whose cost previously grew sharply with scale.
A real page also contains DOM work, layout, networking, component libraries, serialization, and business computation. Evaluate an upgrade using your own flame charts, interaction latency, memory curves, and bundle output—not a framework microbenchmark transplanted into an application promise.
Which projects are most likely to notice a benefit?
- Data dashboards with many interdependent computed values.
- Editors with frequent mutations, filtering, and derived state.
- Long-running workbenches that create and destroy many effects.
- Applications with many form fields and complex validation dependencies.
- State-management or utility libraries built on Vue’s reactive primitives.
Ordinary content pages receive the internal improvements too, but users may not perceive a dramatic difference.
Who should be especially cautious?
Migration risk is relatively contained when code uses only the public ref, reactive, computed, watch, and effectScope APIs. The real warning signs are:
- Reading or mutating Vue’s private reactive fields directly.
- Depending on unpublished effect, Dep, or scheduler implementation details.
- Monkey-patching reactive functions.
- Relying on implicit watcher order, synchronous flush behavior, or cleanup timing.
- Supporting several Vue minor versions in a library without boundary tests.
The purpose of an internal rewrite is precisely to let Vue change implementation while preserving its public API. Conversely, code tied to internals is the code most likely to break during this kind of upgrade.
Change two: Vapor Mode is no longer only an experimental idea
Vapor Mode is a new compilation mode for Vue SFCs. A conventional Vue template generates VNodes; the Virtual DOM runtime compares the previous and next trees and applies their differences to the real DOM. Vapor lets the compiler identify dynamic regions ahead of time and generate more direct, fine-grained update logic.
It has two goals:
- Reduce the application’s baseline bundle size.
- Reduce the runtime cost of creating, traversing, and comparing VNodes.
The official release notes say Vapor reaches a level comparable with Solid and Svelte 5 in the third-party js-framework-benchmark. That statement should also be read as a benchmark result, not as a single ranking that represents every real application and framework.
Vapor does not turn on automatically after an upgrade
Vue 3.6 still uses the VDOM by default. An SFC must opt into Vapor explicitly:
<script setup vapor lang="ts">
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">
Count: {{ count }}
</button>
</template>
Two equivalent entry points are also supported:
<script vapor>
// Shorthand for <script setup vapor>
</script>
<template vapor>
<!-- Compile the entire SFC with Vapor -->
</template>
This means a team can upgrade to 3.6, keep its existing VDOM application, and selectively migrate a component or page later.
Pure Vapor applications
If an application consists entirely of Vapor components, it can use:
import { createVaporApp } from 'vue'
import App from './App.vue'
createVaporApp(App).mount('#app')
This mode can omit the Virtual DOM runtime and is therefore the clearest route to a smaller baseline bundle. It is best suited to small new applications, interactive tools, or independent pages with a tightly controlled technology stack.
Using Vapor inside an existing VDOM application
An existing createApp() project needs the interoperability plugin:
import { createApp, vaporInteropPlugin } from 'vue'
import App from './App.vue'
createApp(App)
.use(vaporInteropPlugin)
.mount('#app')
A Vapor application can also install the plugin to host VDOM components, but doing so brings the VDOM runtime back and weakens the bundle-size advantage.
Components written with Render Functions or JSX remain VDOM components. They also require interoperability inside a pure Vapor application.

My recommendation matches the official guidance: organize the two rendering modes into clearly bounded regions instead of nesting them back and forth at every level.
For example, a high-frequency data canvas could become a Vapor page or subtree while the surrounding admin navigation, dialogs, and mature component library remain on the VDOM. The gain, failure radius, and rollback path are all easier to measure that way.
The easiest rc.2 trap: event delegation became opt-in
This section is especially important if you already tried Vapor during rc.1.
In rc.1, eligible events were automatically delegated to document. That behavior differs from native DOM and standard Vue semantics: if an ancestor calls stopPropagation() first, the event never reaches document, so the child’s delegated handler never runs.
Starting with 3.6.0-rc.2, Vapor binds DOM listeners directly to elements by default. Add the Vapor-specific .delegate modifier only when you explicitly want document-level delegation:
<button @click.delegate="onClick">
Save
</button>
The old compilerOptions.eventDelegation option has also been removed.
Migration requires two actions:
- Remove any existing
compilerOptions.eventDelegation. - Use
.delegateonly for large numbers of stable listeners whose propagation path has been verified.
This is not an optimization for a global mechanical replacement. Whether delegation is worthwhile depends on node count, lifecycle, and propagation semantics.
Compatibility boundaries between Vapor and standard Vue
Vapor is not intended to cover every historical Vue capability on day one. The official documentation says it supports a subset of the current Vue API. Features that depend on VNodes or public component-instance proxies cannot currently be used directly.
| Capability | VDOM component | Vapor component |
|---|---|---|
| Composition API | Supported | Primary programming model |
| Options API | Supported | Not supported |
app.config.globalProperties | Supported | Not supported |
getCurrentInstance() | Returns the current instance | Returns null |
| Render Function / JSX | Native VDOM | Still VDOM; mixing requires interop |
v-memo | Supported | Not applicable / unsupported |
@vue:xxx element lifecycle events | Supported | Not supported |
| Component template refs | Expose public instance capabilities | Do not expose $el, $props, $attrs, $slots, $refs, and similar properties |
| Custom directives | Standard directive lifecycle object | Vapor-specific function interface |
Do not use slots.default() as a side-effect-free probe
In the VDOM world, some components call slots.default() first, inspect the returned content, and then decide whether to render a fallback. In Vapor, calling a slot may immediately create Blocks and DOM, register reactive effects, or claim existing SSR DOM during hydration.
Do not write this:
<script setup vapor>
import { useSlots } from 'vue'
const slots = useSlots()
const content = slots.default?.()
const showFallback = !content
</script>
It is generally safer to let the template perform the actual slot rendering:
<template>
<slot />
</template>
Custom directives need a separate migration
A Vapor custom directive receives a node and reactive getters, and can return a cleanup function. It is not a direct copy of the mounted, updated, and unmounted object used by a VDOM directive.
If a project or component library depends on many custom directives, list them as separate migration items and verify reactive updates, cleanup, and SSR behavior one by one.
How should we think about Nuxt, Router, Pinia, and component libraries?
Availability in Vue core does not mean the frameworks above it have completed their Vapor integration automatically.
The official Vapor Roadmap tracks Router, Pinia, Nuxt, DevTools, and Vue Test Utils separately. Nuxt also depends on SSR hydration support. In a real project, compiler recognition, server output, client hydration, and component inspection in development tools are all part of one complete chain.
I would therefore divide projects into two groups:
Ordinary Vue / Vite applications
Upgrade to the 3.6 RC on a branch, verify the existing VDOM mode first, and then try Vapor on one leaf page. The dependency chain is relatively direct, so problems are easier to attribute.
Nuxt, SSR, or component-library-heavy applications
Do not force the underlying Vue version to the RC with a package override and call the Vapor migration complete. First confirm explicit support in the framework, build plugins, and SSR pipeline, then test hydration, route transitions, async components, and component-library interop.
Even without Vapor, testing Vue 3.6’s new reactivity core is worthwhile. Keep this experiment on an isolated branch or in a test environment.
A safer upgrade path
Phase one: upgrade dependencies without enabling Vapor
An ordinary Vue / Vite project can install the RC on an experimental branch:
pnpm add vue@3.6.0-rc.2
If the project depends directly on @vue/server-renderer, keep it on the same version as Vue:
pnpm add vue@3.6.0-rc.2 @vue/server-renderer@3.6.0-rc.2
Then verify the original VDOM application first:
- Unit and component tests.
- Initial SSR and client hydration.
- Complex
watch,computed, andeffectScopescenarios. - Effect cleanup after rapid route transitions.
- Custom directives, Teleport, Suspense, and async components.
- Development and production builds.
- Browser console warnings and errors.
- Timing and memory baselines for critical interactions.
If this phase fails, the cause is more likely the new reactivity implementation or an RC regression than Vapor, making diagnosis much easier.
Phase two: choose one clearly bounded region
A good pilot usually has all of these properties:
- It uses the Composition API.
- It rarely depends on component instances.
- It does not rely on a complex VDOM component library.
- It updates frequently and has real performance pressure.
- It has an independent route or a clear component-tree boundary.
- It has a repeatable performance test.
Record JavaScript size, interaction latency, CPU time, and memory before the migration, then enable Vapor. Without a baseline, it is easy to mistake “it runs” for “it was worth migrating.”
Phase three: verify interoperability and SSR
At minimum, cover these combinations:
- A VDOM parent containing a Vapor child.
- A Vapor parent containing a VDOM child.
- Props, emits, and two-way binding.
- Default, named, and scoped slots.
- Suspense, async components, and error boundaries.
- Server output, hydration, and mismatch recovery.
- Watcher, event, and directive cleanup after unmount.
Many fixes in the rc.2 list concentrate on hydration, Suspense, slot anchors, and VDOM interoperability. That shows these areas are converging quickly—and that they deserve the most testing right now.
Phase four: wait for stable before setting a production schedule
An RC means the feature set is close to final and the focus has shifted to regressions and edge cases. It does not mean the ecosystem is fully stable.
A production project should at least meet all of these conditions:
- The stable Vue 3.6 release is available.
- The framework and build tools explicitly support it.
- Core component libraries pass interoperability tests.
- DevTools, testing tools, and the SSR pipeline work.
- The performance gain justifies migration and maintenance cost.
- A rollback to VDOM is always available.
Four things I would not do
1. Do not treat an RC like an ordinary patch
Prerelease behavior can still change. The event-delegation change from rc.1 to rc.2 is the clearest example.
2. Do not migrate every component just to “use Vapor”
Navigation, static content, and low-frequency settings pages may gain very little. Migrate actual bottlenecks before chasing a migration percentage.
3. Do not turn microbenchmarks into application promises
The “more than 30 times” result comes from a specific reactive stress case. “Comparable with Solid and Svelte 5” comes from a third-party benchmark. Both establish technical direction; neither replaces measurement in your project.
4. Do not ignore ecosystem boundaries
Vue core, the SFC compiler, Vite plugins, Nuxt, SSR, Router, Pinia, component libraries, DevTools, and testing tools form the developer experience together. Moving one package to an RC does not upgrade the whole chain.
Recommendations by project type
| Project type | Worth trying now? | Recommendation |
|---|---|---|
| Small new tool or internal experiment | Yes | Try pure Vapor and measure bundle size and interaction |
| Existing Vue SPA | Yes, on a branch | Keep VDOM first, then migrate one performance-sensitive page |
| Nuxt / SSR site | Good for research; wait for production | Confirm framework support and focus on hydration |
| Component library | Begin compatibility testing early | Check instance proxies, slots, custom directives, and interop |
| Large dashboard / editor | Strong candidate for stress testing | Compare the separate gains from alien-signals and Vapor |
| Content-focused site | Lower priority | Wait for stable Vue and a mature ecosystem rather than adding complexity for a limited gain |
Final assessment
The most valuable part of Vue 3.6 RC is not the number of new APIs you can immediately add to application code. It advances two long-term questions at the same time:
- Can Vue’s reactivity system remain efficient in more complex dependency graphs?
- Can Vue templates preserve a familiar development experience without always paying the cost of a Virtual DOM?
alien-signals addresses the first question; Vapor attempts to address the second.
For an existing project, the most sensible strategy is: test 3.6 first as a reactivity-core upgrade, then evaluate Vapor as an optional, local compilation mode. Testing them separately reveals where performance changes come from and keeps the risk inside clear boundaries.
Vue 3.6 is not yet at the stage where everyone should upgrade today, but it has reached the stage where every Vue team should begin to understand it.
Sources and notes
- Vue 3.6 minor branch Changelog: the Vapor overview and compatibility boundaries from 3.6.0-rc.1, plus the event-delegation change in rc.2.
- Vue 3.6.0-rc.2 Release: the latest release candidate when this article was published.
- Vue 3.5 release notes: the baseline for reactivity improvements, SSR, and developer APIs in Vue 3.5.
- Vue core: alien-signals refactor PR: the reactivity implementation, memory benchmark, and selected stress-case microbenchmarks.
- Vapor Mode Roadmap: the roadmap for core capabilities and Vue ecosystem integration.
- js-framework-benchmark: the third-party frontend benchmark cited by the Vue release notes.
The upgrade recommendations in this article are my judgment based on official material and engineering experience. AI assisted with source organization and language review; each project should still rely on its own test results.
Comments
Sign in to join the discussion
Your email remains private; only your display name appears.