Vapor Mode v0.6

Vapor mode is Vue's compilation-based rendering mode (Vue 3.6): templates compile to code that manipulates elements directly — no Virtual DOM, no per-component diffing. Vue Lynx runs it on Lynx's dual-thread architecture. On the update path it is 5.8–9.8× faster on the background thread than VDOM (2.1–6.3× end-to-end), with creation at parity and roughly half the cross-thread traffic, for about +26% bundle gzip — full numbers (and an interactive playground; ReactLynx as reference) in the framework bench.

Experimental

Vapor mode itself is not part of any stable Vue release yet — as of mid-2026 it ships only in the Vue 3.6 beta / prerelease line. Vue Lynx pins vue@3.6.0-beta.17 and inherits that status.

Enabling Vapor mode

Vapor is a per-app, build-time choice. Turn it on in three places: the plugin flag, the vue-lynx/vapor entry, and the vapor attribute on each <script setup>:

lynx.config.ts
import { defineConfig } from '@lynx-js/rspeedy'
import { pluginVueLynx } from 'vue-lynx/plugin'

export default defineConfig({
  plugins: [
    pluginVueLynx({
      vapor: true,
    }),
  ],
})
src/index.ts
import { createApp } from 'vue-lynx/vapor'
import App from './App.vue'

createApp(App).mount()

import { ref } from 'vue' keeps working — with vapor: true the plugin aliases 'vue' to vue-lynx/vapor. That entry is the Vapor runtime plus Lynx DOM-compat layer (not the default vue-lynx VDOM entry). Full export list: vue-lynx/vapor API reference.

The demo below is the repository's Vapor example — same three changes, live on this page:

This site can flip many examples between VDOM and Vapor with the VDOM | Vapor control in the nav (and the badge in each example's footer). Coverage and unsupported reason codes are listed in the example support appendix.

Instant first-frame rendering

Vapor in 0.6 supports Instant First-Frame Rendering (IFR) — paint synchronous content during loadTemplate before the background thread starts. Enable it next to vapor:

lynx.config.ts
pluginVueLynx({
  vapor: true,
  enableIFR: true,
})

Vapor IFR uses the compiled template() / tree protocol (not VDOM Element Templates). Product thesis and enablement: IFR · measured trade-offs: IFR benchmarks.

Limitations

Same as upstream Vapor

These match Vue's Vapor constraints today:

  • No Options API<script setup> only; getCurrentInstance() returns null.
  • Not yet shipped upstream: <Transition>, <Teleport>, <KeepAlive>, <Suspense>, and other VDOM-only APIs (h(), …). They are absent from vue-lynx/vapor and fail at build time if used by mistake.

Lynx also has no HTML string surface, so v-html and SSR / HTML hydration are unavailable (same as VDOM-mode Vue Lynx). Dual-thread IFR hydration is a separate mechanism.

One app, one mode

Vue Lynx does not mix VDOM and Vapor in the same app (vaporInteropPlugin is not wired up — upstream ties it to the browser DOM renderer). Mode is a strict either/or, so Vue Lynx ships two pure entries instead of one entry pruned by tree-shaking:

EntryContainsUse for
vue-lynxVDOM renderer + Vue runtime-coreRegular (default) apps
vue-lynx/vaporVapor runtime + Lynx DOM-compat layerVapor apps

Appendix

How it works on Lynx

Upstream @vue/runtime-vapor expects a DOM. Vue Lynx gives the background thread a DOM-compatible ShadowElement surface; compiled Vapor code calls normal DOM APIs, which emit the same ops stream to the main thread as VDOM mode:

vapor component → vue-lynx/vapor → @vue/runtime-vapor
  → DOM-compat ShadowElement → ops → Main Thread

Lynx-specific bits: template strings become cloneable prototypes; document event delegation is off (per-element events); @tap.stop maps to catchEvent; scoped CSS uses scope classes instead of data-v-*.

To keep clone-heavy lists cheap across threads, Vue Lynx adds two ops:

  • REGISTER_TREE — static structure once per template
  • CLONE_TREE — one op per instance (uids assigned the same way on both threads)

On create-1k that cuts traffic from ~17k ops / 327 KB (VDOM) to ~7k ops / 160 KB (Vapor).

Example support

Every example on this site is checked against both renderers. Unsupported reason codes map to the limitations above.