Skip to main content
Version: next

Installation

You can load the widget two ways: the bare embed (one script tag, no dependency) or the Vue wrapper (@monoverse/voicebot-vue). Most Vue apps want the wrapper.

Option A — the wrapper

Install

npm install @monoverse/voicebot-vue

vue (^3.4) is a peer dependency.

Render the widget

Render <VoiceBotWidget> once, near the root of your app. It renders nothing visible itself — the widget UI is mounted by the deployed bundle it loads into its own Shadow DOM host.

<script setup lang="ts">
import { VoiceBotWidget } from '@monoverse/voicebot-vue';
</script>

<template>
<YourApp />
<VoiceBotWidget public-key="pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
</template>

The widget core is a one-per-page singleton: the wrapper reuses any VoiceBot script already on the page, so rendering it more than once never injects a second script or opens a second WebSocket/microphone.

Voice or chat

<VoiceBotWidget public-key="pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" variant="voice" />

variant="voice" (the default) loads widget-voice.js.

Props & events

Prop / eventTypeDefaultDescription
public-keystring (required)Your pk_ publishable key.
variant'voice' | 'chat''voice'Which widget bundle to load.
langstringForce the widget UI language (e.g. 'uk', 'en').
script-srcstringhttps://api.monoverse.tech/widgetOverride the CDN base (self-hosted API).
on-actionVoiceBotActionHandlersCart/variant/filter handlers. See Capabilities.
capabilitiesVoiceBotCapability[]Narrow the server-granted capability set for the current page.
tool-handlersVoiceBotToolHandlersLow-level override for canonical action names.
@error(error: Error) => voidEmitted on script load failure or an empty key.
@navigate(target: VoiceBotNavTarget) => voidEmitted on any bot navigation action. Use for SPA client-side routing.

Error handling is a Vue event, not a prop:

<VoiceBotWidget public-key="pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" @error="(e) => console.error(e)" />

Composable (imperative)

For custom layouts, useVoiceBot runs the same lifecycle without rendering an element. Call it inside setup() — it registers onMounted / onBeforeUnmount internally:

<script setup lang="ts">
import { useVoiceBot } from '@monoverse/voicebot-vue';

useVoiceBot({ publicKey: 'pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', variant: 'chat' });
</script>

The composable takes options in camelCase (publicKey, scriptSrc) and accepts an onError callback directly; the <VoiceBotWidget> component uses Vue-idiomatic kebab-case props and the @error event.

Reactivity

The wrapper reads props at mount and is one-shot: it deliberately does not re-inject when a prop changes (that would mint a duplicate token + WebSocket). To switch the key at runtime, remount the host — a :key change or a v-if toggle.

Option B — the bare embed (no dependency)

If you'd rather not add a package, drop the deployed bundle in with a single script tag. This is the same component shape, without @monoverse/voicebot-vue — inject in onMounted, tear down in onBeforeUnmount:

VoiceBotEmbed.vue
<script setup lang="ts">
import { onBeforeUnmount, onMounted } from 'vue';

const props = withDefaults(
defineProps<{
publicKey: string;
apiBase?: string;
variant?: 'voice' | 'chat';
}>(),
{ variant: 'voice' },
);

let script: HTMLScriptElement | null = null;

onMounted(() => {
const base = props.apiBase ?? 'https://api.monoverse.tech';
script = document.createElement('script');
script.src = `${base}/widget/widget-${props.variant}.js`;
script.async = true;
script.dataset.publicKey = props.publicKey;
if (props.apiBase) script.dataset.apiBase = props.apiBase;
document.body.appendChild(script);
});

onBeforeUnmount(() => {
(window as { VoiceBot?: { destroy?: () => void } }).VoiceBot?.destroy?.();
script?.remove();
script = null;
});
</script>

<template>
<!-- The widget renders into its own Shadow DOM host; this component renders nothing. -->
</template>

The full reference lives in examples/embed/vue/.

Nuxt / SSR

The component is SSR-safe: it injects the script in onMounted (which never runs on the server) and the embed itself guards window/document, so a server render is a no-op that never throws. On Nuxt, render it client-side only so the widget code runs only in the browser:

<template>
<ClientOnly>
<VoiceBotWidget public-key="pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
</ClientOnly>
</template>

The package source, props table, and changelog live in the package README on npm.