What is Vue Lynx?
Vue Lynx is a Vue 3 custom renderer for Lynx, enabling you to build native Lynx applications using Vue's familiar Composition API, single-file components, and reactive data model.
If you know Vue 3 and are familiar with Lynx, you already know how to use Vue Lynx.
Main Features
- Familiar Vue 3 API — Use
ref(),computed(),v-for,v-if, SFC<script setup>, and everything you love about Vue. - Cross-Platform Native Rendering — Lynx provides a high-performance native rendering engine on mobile and desktop, as well as first-class Web rendering. Your UI is never a WebView.
- Dual-Thread Architecture — Vue runs on the background thread; the main thread handles native rendering. This separation keeps your app responsive. For latency-sensitive interactions (drag, scroll), Main Thread Script lets you mark functions with
'main thread'to run directly on the main thread with zero cross-thread delay.
For Vue Web Developers
If you're coming from Vue on the web, most of what you know carries over. Here are the key differences.
Change your import
Vue Lynx re-exports the Vue API, so you can replace vue with vue-lynx:
Different elements
Lynx provides native elements instead of HTML. Use <view>, <text>, <image>, etc. in place of <div>, <span>, <img>:
Text content must be wrapped in <text> — Lynx does not support bare text nodes inside <view>.
Different events
Lynx uses its own event system. Use :bindeventname for events that propagate, and :catcheventname for events that don't:
See the Lynx event handling guide for more details.
No document or window
Lynx does not provide browser globals. Libraries that depend on document or window need adaptation to work. Instead, Lynx provides its own APIs via the lynx global object:
- Global APIs like
setTimeout()andconsole.log()are available on both threads, same as the web. - DOM-like queries (e.g.
querySelector,getBoundingClientRect) are available as async background-thread APIs or synchronous Main Thread APIs depending on which thread you need them on. - Platform-native capabilities are available via NativeModules and Custom Elements.
Understanding the Dual-Thread Model
If you've worked with Web Workers, Lynx's architecture will feel familiar. Vue runs on a background thread while native rendering happens on the main thread — just like offloading work to a Web Worker so the UI thread stays responsive. The difference is that in Lynx, this is the default — Vue reconciliation never blocks the main thread. (See Use the Main Thread Responsibly for Interactivity for the design rationale.)
Under the hood, Vue Lynx uses a custom renderer that builds a ShadowElement tree on the background thread. Changes are batched as ops and flushed to the main thread. User interactions flow back as events. This forms a cycle analogous to the browser event loop:
One full cycle — ops down, events back up — is one tick. Vue Lynx's nextTick resolves after a complete cycle, so by the time your callback fires, native elements are fully materialized. This is the same mental model as Vue's nextTick on the web (wait for DOM update), just extended across threads.
Vue API: fully compatible
Vue Lynx reuses @vue/runtime-core directly. Reactivity, component model, lifecycle hooks, template directives — everything works as documented in the Vue 3 docs. See Vue Compatibility for a detailed feature-by-feature breakdown.
Caveats for the dual-thread environment
A few behaviors are adjusted because your code and native elements live on different threads.
Lifecycle runs on the background thread — use nextTick to wait for the main thread
Just like on the web, onMounted means "the component tree is built." The difference is that native elements are created asynchronously on the main thread. If you need to interact with native elements after mount, wait one tick:
If your onMounted only sets up reactive state, timers, or data fetching, no nextTick is needed — those don't depend on native elements.
Background thread has only async access — use Main Thread Script for synchronous needs
From the background thread, native element access is always asynchronous. Template ref returns a ShadowElement (a background-thread reference to the native element), and layout queries like getBoundingClientRect() require an async API:
When you need synchronous access — smooth animations, gesture handling, layout measurement — use Main Thread Script. Functions marked 'main thread' run directly on the main thread and can call Main Thread APIs like getComputedStyleProperty(), listen to main-thread-bindlayoutchange, and access native elements via useMainThreadRef():
See Vue Features Compatibility for feature-specific caveats and unsupported features.
Next Steps
- Vue Compatibility — Feature-by-feature breakdown of what works and what differs
- Main Thread Script — Run performance-critical logic on the main thread
- Tutorial: Product Gallery — Build a waterfall gallery with tap-to-like, auto-scroll, and MTS scrollbar
- Tutorial: Product Detail — Build a touch-swipeable image carousel with snap animation