Skip to main content

Vue

@webview-ts/vue provides the same surface as the React binding, as a Vue 3 plugin plus composables. Both bindings subscribe to the same shared state machines — behavior is identical.

Setup

bridge.ts
import { createBridgeVue } from '@webview-ts/vue';
import { camera, location } from './plugins';

export const bridge = createBridgeVue({
plugins: [camera, location],
});
main.ts
import { createApp } from 'vue';

createApp(App).use(bridge).mount('#app');

usePlugin

Each action handle exposes the same fields as React, wrapped in Refs for reactivity:

<script setup lang="ts">
import { usePlugin } from '@webview-ts/vue';
import { camera } from './plugins';

const { takePhoto } = usePlugin(camera);
// takePhoto.status → Ref<'idle' | 'loading' | 'success' | 'error'>
// takePhoto.data → Ref<Response | null>
// takePhoto.error → Ref<BridgeCallError | null>
// takePhoto.isLoading → Ref<boolean>

async function shoot() {
const result = await takePhoto.execute({ quality: 0.9 });
// ^? typed from the contract
}
</script>

<template>
<button :disabled="takePhoto.isLoading.value" @click="shoot">Take Photo</button>
<img v-if="takePhoto.data.value" :src="takePhoto.data.value.uri" />
</template>

Typed event subscription uses short names, with automatic cleanup on scope dispose:

const { on } = usePlugin(location);
on('updated', (pos) => {
position.value = pos;
});

The host role — a Vue page as an iframe shell

The web is always both. A Vue app can host embedded iframes with the useBridgeHost composable — same neutral core factory, transport injected:

import { useBridgeHost, IframeHostAdapter } from '@webview-ts/vue';
import { shell } from './plugins';

const { sendEvent } = useBridgeHost({
adapter: new IframeHostAdapter(frameEl, CHILD_ORIGIN),
plugins: [shell.host(handlers)],
});

Teardown happens automatically on scope dispose. See Iframe embeds for the full pattern.

useAction / useEvent

Same semantics as the React equivalents, with Ref-wrapped state.