Vue 3.6 hit rc.10 last week with Vapor Mode declared feature-complete, and the stable release is coming Q4 2026. The short version: add one attribute to a <script setup> block, and the compiler stops generating a virtual DOM tree for that component entirely. No VNode objects. No diffing pass. Direct DOM updates, wired to reactive state at build time. In the js-framework-benchmark, Vapor now scores within 2% of Solid.js and Svelte 5 — frameworks that were built around this model from day one. Vue just caught up.
What Vue 3.6 Vapor Mode Actually Does
The virtual DOM was always a clever workaround, not a first principle. You describe what the UI should look like, the framework builds a cheap JS object tree, diffs it against the previous tree, then flushes the diff to the real DOM. Works fine until you have a 500-row table that updates 30 times per second, and suddenly you’re spending 40% of your frame budget just producing VNode garbage for the GC to clean up.
Vapor Mode moves that work to the compiler. When you mark a component as vapor, the Vue compiler analyzes your template and generates code that directly wires reactive state to the specific DOM nodes that depend on it. When count changes, only the text node rendering count updates — no reconciliation, no tree walk, no object allocation. The same model Solid.js has used since 2019, now available in Vue without switching frameworks.
Vue 3.6 also ships a full rewrite of @vue/reactivity powered by alien-signals — a push-pull algorithm that propagates dirty flags through the dependency graph without executing any computations until they’re needed. That delivers 4x faster reactive updates and 60% lower memory usage in complex UIs, with zero breaking changes. Even if you never add a single vapor attribute, updating to 3.6 is worth it for the reactivity improvements alone.
The Performance Numbers
Concrete benchmark results from Vapor RC testing:
- 200-row data table with frequent cell updates: 40–55% faster than Vue 3.5
- Memory allocation during updates: down ~60%
- 1,000-row DOM update: ~27ms — 36% faster than React 19.2
- Runtime bundle for an all-Vapor app: ~16 KB gzipped, down from 33 KB for standard Vue 3.5
Solid.js still has a runtime footprint advantage (~6 KB) and Svelte 5 is barely measurable (~2 KB compiled), but the raw DOM operation speed gap is now closed. If your bottleneck is rendering throughput rather than initial download size, Vapor erases the historical argument for migrating away from Vue.
How to Opt In to Vue Vapor Mode Today
Three ways to mark a component as Vapor:
<!-- Method 1: attribute on script tag -->
<script setup vapor>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>
<!-- Method 2: attribute on template tag -->
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template vapor>
<button @click="count++">{{ count }}</button>
</template>
<!-- Method 3: file naming convention -->
<!-- Rename the file to Counter.vapor.vue -->
For existing apps built on createApp(), add vaporInteropPlugin so Vapor components can coexist with your standard VDOM tree:
import { createApp } from 'vue'
import { vaporInteropPlugin } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.use(vaporInteropPlugin)
app.mount('#app')
This is the smart migration path. Identify your hot-path components — data tables, dashboards, infinite lists — and opt them in one at a time. Everything else stays on the VDOM. No big-bang rewrite, no migration weekend.
What Breaks in Vapor Mode
Vapor is Composition API only. Before adding that attribute, audit your component for these blockers:
- Options API:
data(),methods,computedas options, and mixins are not supported. Migrate to<script setup>first. getCurrentInstance(): Returnsnullin Vapor components. Any library that reaches for the internal instance handle will break silently.app.config.globalProperties: Not visible in Vapor components. Move$http,$filters, and i18n helpers toprovide/injector explicit imports.v-memo: Not supported — redundant in Vapor since every DOM update is already surgical.$eland$refs: Not exposed on component template refs.
Ecosystem Status: What’s Ready, What Isn’t
Pinia works. Nuxt 4.6 added Vapor interop mode — set vue: { vapor: true } in nuxt.config.ts and the app root stays on the VDOM while individual components opt in. That’s enough to start experimenting in a production Nuxt app today.
Vue Router doesn’t fully support Vapor yet. Vue DevTools is getting a Vapor panel, but it’s not there yet. Vue Test Utils is on the Vapor roadmap. The practical guideline: use Vapor in leaf components — the ones deep in your tree with no router-view or global injection concerns. Skip it at the app-shell level for now.
The Bottom Line
Vue 3.6 isn’t a rewrite. It’s Vue 3.5 with a compiler that knows how to stop leaning on the virtual DOM when you tell it to. The alien-signals reactivity rewrite is a free performance upgrade for every app on upgrade. Vapor Mode is the opt-in path for the components where VDOM overhead was always the bottleneck.
Start with one component. A data table, a list, anything that re-renders under load. Add vapor to the script tag, run your benchmark, and let the number decide. The full Vue 3.6 changelog is public, the stable release is coming this quarter, and the upgrade path is as conservative as you want it to be.




