# Vue guide

> How to write SymbioteNative apps with the Vue adapter.

The Vue adapter lets Vue 3 drive the same native Fabric engine as React,
Angular, Svelte, and Solid. The native output is not DOM and not a WebView:
Vue renderer operations become engine mutations, then Fabric commits real
native views.

## SFC or JSX — your choice

Vue components can be authored as a `.vue` single-file component or as Vue
JSX/TSX (`@vue/babel-plugin-jsx` compiling to `createVNode` calls) — both
compile down to the exact same `@vue/runtime-core` vnode tree, so both drive
the identical adapter, the identical component surface, and the identical
native output. Neither is a reduced mode of the other.

<video
  poster={`${import.meta.env.BASE_URL}/vue-demo.webp`}
  aria-label="Vue SFC driving a native SymbioteNative demo"
  width="260"
  height="564"
  preload="none"
  autoplay
  muted
  loop
  playsinline
>
  <source src={`${import.meta.env.BASE_URL}/vue-demo.mp4`} type="video/mp4" />
  <source src={`${import.meta.env.BASE_URL}/vue-demo.webm`} type="video/webm" />
</video>

_Vue SFC (`.vue`) — the reference canary at `examples/vue-sfc`._

<video
  poster={`${import.meta.env.BASE_URL}/vue-tsx-demo.webp`}
  aria-label="Vue TSX driving a native SymbioteNative demo"
  width="260"
  height="564"
  preload="none"
  autoplay
  muted
  loop
  playsinline
>
  <source
    src={`${import.meta.env.BASE_URL}/vue-tsx-demo.mp4`}
    type="video/mp4"
  />
  <source
    src={`${import.meta.env.BASE_URL}/vue-tsx-demo.webm`}
    type="video/webm"
  />
</video>

_Vue JSX/TSX (`.tsx`) — the same canary, authored render-function-first at
`examples/vue-tsx`, proving the component surface is template-agnostic._

## Minimal component

```vue
<script setup lang="ts">
import { ref } from 'vue';

const count = ref(0);
</script>

<template>
  <view class="root">
    <pressable @press="count++" v-slot="{ pressed }">
      <text :class="pressed ? 'pressed' : 'label'"> Count is {{ count }} </text>
    </pressable>
  </view>
</template>

<style scoped>
.root {
  flex: 1;
  align-items: center;
  justify-content: center;
}
.label {
  font-size: 24px;
}
.pressed {
  font-size: 24px;
  opacity: 0.6;
}
</style>
```

`view`, `pressable`, and `text` are intrinsic tags — no import needed. Only a
composed component (`FlatList`, `SectionList`, `VirtualizedList`,
`VirtualizedSectionList`, `KeyboardAvoidingView`, …) still comes from
`@symbiote-native/vue`.

`StyleSheet.create` works identically if you'd rather keep style objects
inline instead of an SFC `<style>` block — see the
[Styling guide](/docs/learn/styling/) for both paths.

## Vue contract

Vue uses Vue-shaped APIs where the framework semantics differ from React:

- framework events are typed emits such as `@press` and `@value-change`
  (shared by `Switch` and `TextInput`);
- dynamic children use slots and scoped slots instead of React render props;
- template attributes can use kebab-case, and the adapter folds them to the
  native camelCase contract before commit;
- imperative handles are exposed through Vue template refs;
- controlled-value components (`Switch`, `TextInput`, the [Slider](/docs/packages/slider/)
  wrapper) support real Vue `v-model`, alongside the explicit `value`/`@change-*` pair;
- `v-show` works on any SymbioteNative host node — it toggles native `display: none`
  instead of unmounting, same semantics as Vue's DOM `v-show`.

```vue
<switch v-model="enabled" />
<text-input v-model="text" />
<view v-show="visible">
  <text>Hidden without unmounting</text>
</view>
```

`v-model` is sugar over the same contract that was already there: `Switch`
and `TextInput` both still accept the explicit `:value`/`@value-change` pair
— `TextInput`'s `@value-change` fires with `(text, event)`, so the raw native
event is still reachable without a separate handler. Bare `v-model="x"`
compiles to `:model-value="x"` + `@update:model-value`; every model-capable
component also accepts the named form `v-model:value="x"`. Mixing `v-model`
with the explicit `value`/`@value-change` pair on the same element is
redundant but harmless — both fire.

Read the exact API surface in the [Vue API reference](/docs/api/vue/).
