"What is coming in Vue?" is a warm-up question with a trap in it: Vue 3.6 with Vapor mode has been "almost here" for two years, and candidates who say it has shipped get corrected. As of September 2026 the stable release is 3.5.43 and 3.6 is at release candidate 9. Know what each version does, and say "release candidate" until you have checked the tags.
const { size = 'md' } = defineProps<Props>() keeps reactivity.useTemplateRef(), useId(), onWatcherCleanup(), deep: number on watchers, pause()/resume() on watcher handles.hydrateOnVisible, hydrateOnIdle, hydrateOnInteraction, hydrateOnMediaQuery), surfaced in Nuxt as hydrate-on-* props on Lazy components.data-allow-mismatch, useId stability, and Teleport defer.app.config.throwUnhandledErrorInProduction and Trusted Types support.<script setup vapor> component compiles to code that creates DOM nodes once and binds reactive expressions to them directly, in the style of Solid or Svelte 5. It is opt-in per component, interoperates with regular virtual-DOM components through an interop layer, supports a subset of the API (<script setup> and Composition API; no Options API), and shrinks the baseline bundle because the runtime it needs is smaller.vue: { vapor: true } option (it installs Vue's vaporInteropPlugin), which needs a Vue 3.6 release candidate installed. The application root stays virtual DOM and you opt in per component with <script setup vapor>. Documented limitations: no full-Vapor apps yet, template refs on Vapor components expose no $el, the Options API (and therefore defineNuxtComponent's asyncData) is unavailable inside them, some built-ins cannot inspect Vapor slot children, and Nuxt composables called after an await may lose the Nuxt context. Try it in a fresh project, not by flipping the flag on a production app.@vue/devtools-api alongside it, and @nuxt/test-utils 4 runs on Vitest 4 or 5 (Vitest 5 shipped in September 2026).A component in the two modes. The template is identical; only the compile target differs:
<script setup lang="ts">
const count = ref(0)
const double = computed(() => count.value * 2)
</script>
<template>
<button type="button" @click="count++">
{{ count }} × 2 = {{ double }}
</button>
</template>
<script setup lang="ts" vapor>
const count = ref(0)
const double = computed(() => count.value * 2)
</script>
<template>
<button type="button" @click="count++">
{{ count }} × 2 = {{ double }}
</button>
</template>
What the compiler produces conceptually for the Vapor version: create the button once, then bind each expression to the text node that displays it.
const button = template('<button type="button"> </button>')()
const text = button.firstChild!
renderEffect(() => { text.data = `${count.value} × 2 = ${double.value}` })
button.addEventListener('click', () => count.value++)
No vnode tree, no diff: when count changes, the one effect that reads it updates the one text node.
And the 3.5 APIs a component written today should already use:
<script setup lang="ts">
const { placeholder = 'Search' } = defineProps<{ placeholder?: string }>()
const query = defineModel<string>({ default: '' })
const input = useTemplateRef<HTMLInputElement>('input')
const id = useId()
watch(query, (q) => {
const controller = new AbortController()
onWatcherCleanup(() => controller.abort())
void $fetch('/api/search', { query: { q }, signal: controller.signal })
})
defineExpose({ focus: () => input.value?.focus() })
</script>
<template>
<label :for="id">{{ placeholder }}</label>
<input :id="id" ref="input" v-model="query" type="search">
</template>
"We could switch the app to Vapor and cut the bundle in half" is not a sentence to say in an interview in 2026. Vapor is per component, opt-in, in release candidate, and Nuxt integration follows Nuxt's own schedule. The credible version: "I would try 3.6's reactivity core first, because everyone gets it, then evaluate Vapor on isolated leaf components in Nuxt's interop mode, which is experimental and needs the 3.6 release candidate."
Whether 3.6.0 stable has shipped, the exact Vapor API surface (vapor attribute, the interop plugin) and the current list of limitations of Nuxt's vue.vapor interop mode. Read the release notes and this docs section the week of the interview.
vue@3.6.0-rc.x in a scratch Vite project, write the Counter above in Vapor mode, and inspect the compiled output in the Vite dev server. Compare bundle sizes of a Vapor app and a virtual-DOM app with one component each."An opt-in compilation strategy in Vue 3.6 that compiles a component's template to direct DOM operations bound to reactive expressions, instead of producing virtual DOM that gets diffed. You enable it per component with <script setup vapor>, it uses the Composition API subset, and it interoperates with normal components, so you can adopt it on leaf components first. The win is smaller baseline runtime and fine-grained updates. Underneath, 3.6 also swaps the reactivity core for an alien-signals-based push-pull algorithm, and that part every component gets. As of this month 3.6 is a release candidate, so I would describe it as imminent, not shipped; Nuxt 4 can already run Vapor components in interop mode behind an experimental flag."
"Reactive props destructure for defaults without withDefaults, useTemplateRef for explicit template refs, useId for SSR-stable ids in forms and ARIA wiring, onWatcherCleanup for cancelling in-flight requests inside watchers, and the lazy hydration strategies through Nuxt's Lazy components with hydrate-on-visible. Behind the scenes the 3.5 reactivity refactor made deep tracking of large data cheaper, which changed how often I reach for shallowRef."
Nothing yet. Notes filed here show up on this page and the section overview.