@agentdeskbot/vue
v1.0.0
Published
AgentDesk AI chat widget — Vue 3 SDK
Maintainers
Readme
@agentdeskbot/vue
Drop-in Vue 3 & Nuxt 3 SDK for the AgentDesk AI chat widget.
Embed a fully-typed, RAG-grounded support bot in your Vue app in under 30 seconds — no manual onMounted boilerplate, no plugin setup required, SSR-safe for Nuxt out of the box.
Table of Contents
- Features
- Quick Start
- Installation
- Usage by Framework
- Props API
- Events
- Advanced
- How it works
- Troubleshooting
- Migrating to v0.1.0
- Related Packages
- License
Features
- One component, zero config —
<AgentDeskWidget bot-id="…" />is everything you need. - Optional global plugin — Install once, use the component anywhere without per-file imports.
- Nuxt 3 ready — Drop-in client plugin that keeps the widget out of SSR.
- Strictly typed — Full TypeScript surface with IntelliSense, including prop types and emit payloads.
- Zero runtime deps outside Vue —
vueis externalized; no other peer deps required. - Tree-shakable —
"sideEffects": false; ESM and CJS bundles ship with.d.tsdeclarations. - Idempotent injection — Deduplicates scripts so re-mounts (HMR,
<KeepAlive>) don't double-load the widget. - Origin-validated events —
postMessagelisteners verifyevent.originandbotIdbefore emitting.
Quick Start
<script setup lang="ts">
import { AgentDeskWidget } from '@agentdeskbot/vue';
</script>
<template>
<main><!-- your app --></main>
<AgentDeskWidget bot-id="YOUR_BOT_ID" />
</template>That's it. The widget will mount as a floating launcher bubble in the bottom-right corner of your page.
Installation
# npm
npm install @agentdeskbot/vue
# yarn
yarn add @agentdeskbot/vue
# pnpm
pnpm add @agentdeskbot/vuePeer dependencies (automatically installed alongside in modern setups)
| Package | Required | Supported versions |
| --- | --- | --- |
| vue | ✅ | >= 3.0.0 |
Vue 2 users: This package targets Vue 3 only. For Vue 2, use the manual script-tag approach below.
Usage by Framework
Plain Vue 3
<!-- src/App.vue -->
<script setup lang="ts">
import { AgentDeskWidget } from '@agentdeskbot/vue';
</script>
<template>
<main><!-- your app --></main>
<AgentDeskWidget bot-id="YOUR_BOT_ID" />
</template>Vue 3 with global plugin
Register once in your app's entry point and use <AgentDeskWidget> anywhere — no per-file imports needed.
// src/main.ts
import { createApp } from 'vue';
import { AgentDeskPlugin } from '@agentdeskbot/vue';
import App from './App.vue';
createApp(App)
.use(AgentDeskPlugin)
.mount('#app');<!-- Any component — no import required -->
<template>
<AgentDeskWidget bot-id="YOUR_BOT_ID" />
</template>Plugin options
import { AgentDeskPlugin } from '@agentdeskbot/vue';
app.use(AgentDeskPlugin, {
// Set to false to register the plugin without auto-registering the
// global <AgentDeskWidget> component. Useful when you want to manage
// component registration manually.
globalComponent: true, // default
});| Option | Type | Default | Description |
| --- | --- | --- | --- |
| globalComponent | boolean | true | When true, registers <AgentDeskWidget> globally. Set to false to skip global registration. |
Nuxt 3
Create a client-only plugin so the widget is never executed during SSR. The .client.ts suffix tells Nuxt to only load this module on the browser.
// plugins/agentdesk.client.ts
import { AgentDeskPlugin } from '@agentdeskbot/vue';
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(AgentDeskPlugin);
});Then use the component normally in any page or layout:
<!-- app.vue or layouts/default.vue -->
<template>
<div>
<NuxtPage />
<AgentDeskWidget bot-id="YOUR_BOT_ID" />
</div>
</template>Why a
.client.tsplugin? Nuxt runs every plugin inplugins/on the server first. The widget toucheswindowanddocumentduring mount, so it must be deferred to the client. The.client.tssuffix is Nuxt's official escape hatch for this.
Cross-origin / Self-hosted Embeds
If your AgentDesk backend runs on a different domain than the page embedding the widget, point api-origin and script-src at the correct hosts:
<AgentDeskWidget
bot-id="YOUR_BOT_ID"
api-origin="https://support.yourapp.com"
script-src="https://support.yourapp.com/widget.js"
/>| Prop | When you need it |
| --- | --- |
| apiOrigin | The widget is embedded on app.com but the chat backend lives on support.yourapp.com. |
| scriptSrc | Same as above, but for the actual widget.js bundle URL. |
Props API
| Prop (CamelCase) | Prop (kebab-case) | Type | Default | Required | Description |
| --- | --- | --- | --- | --- | --- |
| botId | bot-id | string | — | ✅ | The Bot ID from your AgentDesk dashboard. |
| mode | mode | 'launcher' \| 'inline' | 'launcher' | — | 'launcher' = floating bubble. 'inline' = fills nearest positioned ancestor. |
| scriptSrc | script-src | string | 'https://agentdeskbot.vercel.app/widget.js' | — | URL to compiled widget.js. Override for custom or self-hosted deployments. |
| apiOrigin | api-origin | string | 'https://agentdeskbot.vercel.app' | — | Base URL of your AgentDesk backend. Required for custom or self-hosted deployments. |
| configUrl | config-url | string | {apiOrigin}/api/widget/config/{botId} | — | Fully-qualified override for widget config fetch endpoint. |
| theme | theme | string | — | — | Optional theme name for the widget (e.g. 'webchat-v1'). Mount-only. |
| cspNonce | csp-nonce | string | — | — | Optional CSP nonce to apply to the script tag and dynamic styles. Mount-only. |
| position | position | 'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left' | 'bottom-right' | — | Optional fixed position for launcher bubble and widget pane. |
| className | class-name | string | — | — | Optional custom HTML class name to apply to host element container. |
Both
camelCaseandkebab-caseare fully supported for all props in Vue templates (e.g.,:bot-idand:botIdare equivalent).
WidgetMode reference
import type { WidgetMode } from '@agentdeskbot/vue'; // re-exported from @agentdeskbot/core
type WidgetMode = 'launcher' | 'inline';'launcher'— A floating bubble anchored to the bottom-right corner of the viewport. Clicking it expands the chat surface.'inline'— The widget fills the nearest positioned ancestor element (position: relative | absolute | fixed). Useful for embedding the chat directly inside a page section, side panel, or modal.
Events
The component emits six lifecycle events corresponding to widget actions and network status. Internally, these events are received from the widget's postMessage channel, origin-validated, and re-emitted:
<script setup lang="ts">
function handleOpen() {
analytics.track('agentdesk_widget_opened');
}
function handleClose() {
analytics.track('agentdesk_widget_closed');
}
function handleReady() {
console.log('Widget is ready');
}
function handleError(err: { message: string }) {
console.error('Widget load error:', err.message);
}
function handleMessageSent(msg: { text: string }) {
analytics.track('agentdesk_message_sent', { text: msg.text });
}
function handleInjected() {
console.log('Widget element injected into the DOM');
}
</script>
<template>
<AgentDeskWidget
bot-id="YOUR_BOT_ID"
@open="handleOpen"
@close="handleClose"
@ready="handleReady"
@error="handleError"
@message-sent="handleMessageSent"
@injected="handleInjected"
/>
</template>| Event | Payload | Description |
| --- | --- | --- |
| @open | — | Emitted when the user opens the chat widget. |
| @close | — | Emitted when the user closes the chat widget. |
| @ready | — | Emitted when configuration loads successfully and widget is ready. |
| @error | { message: string } | Emitted when configuration fails to load or socket connection is lost. |
| @message-sent | { text: string } | Emitted when the user/customer sends a message. |
| @injected | — | Emitted when the custom element host is injected into the DOM. |
The SDK stores its own internal event listeners in the
setup()closure and tears them down inonBeforeUnmount, so re-mounting the component (HMR,<KeepAlive>, route changes) is fully idempotent.
Advanced
Multiple bots on the same page
The SDK deduplicates scripts by botId, so mounting two <AgentDeskWidget> instances with different botId values will inject two independent widget bundles:
<template>
<AgentDeskWidget bot-id="SALES_BOT_ID" mode="inline" />
<AgentDeskWidget bot-id="SUPPORT_BOT_ID" mode="launcher" />
</template>Mounting the same botId twice (e.g. across HMR boundaries or after a route change) is a no-op — only the first injection runs, and both components' listeners are registered to receive events.
Recommended Multi-Bot Patterns
When mounting multiple bots on the same page:
- Avoid Launcher Overlaps: Do not mount more than one bot in
launchermode at the same position. If you must have multiple launchers, use thepositionprop to space them out (e.g., one on thebottom-rightand one on thebottom-left). - Prefer Inline Mode: For secondary assistants or context-specific support, prefer
mode="inline"to render the bot within a sidebar, dashboard tab, or modal, keeping the main floating launcher clean. - Event Attribution: Use
@message-sentand@openevent listeners on each widget to attribute user interactions and analytics tracking to the specificbot-id.
Programmatic control
The component renders an empty host <span> and owns the widget lifecycle internally. If you need imperative APIs (open/close, send messages), use the manual script-tag approach and call into the custom element directly:
const el = document.querySelector('agentdesk-widget') as HTMLElement & {
open?: () => void;
close?: () => void;
};
el.open?.();Manual script-tag fallback
If you can't (or don't want to) install the Vue package, drop the script directly into your HTML:
<script
src="https://agentdeskbot.vercel.app/widget.js"
data-bot-id="YOUR_BOT_ID"
data-theme="webchat-v1"
data-mode="launcher"
async
></script>The same data-* attributes used by the Vue SDK are honored by the script.
How it works
Under the hood, the Vue component:
- Deduplicates — On
onMounted, it scansdocumentfor existingscript[data-agentdesk]tags and bails early if one for the currentbotIdis already present. - Injects — Otherwise it appends a
<script>element pointing towidget.jsand tags it withdata-agentdeskanddata-bot-id. - Listens — It registers a single global
messageevent listener onwindowand forwards events to active components matching thebotIdand origin checks. - Cleans up — On
onBeforeUnmountoronDeactivated(for KeepAlive), it removes its listener registry. If the last component referencing abotIdunmounts, the script tag and custom element are removed from the DOM.
Vue tree mount
└─ <AgentDeskWidget :bot-id="…" />
├─ injects <script data-agentdesk data-bot-id="…" src="https://agentdeskbot.vercel.app/widget.js" />
├─ registers component callback in global dispatch Set
└─ renders <span data-agentdesk-vue-host aria-hidden="true" />Troubleshooting
You forgot the .client suffix on your plugin file. Rename it so Nuxt only loads it on the client:
- plugins/agentdesk.ts
+ plugins/agentdesk.client.tsVue plugins that touch browser DOM globals (like window and document) must be client-only. Nuxt recognizes .client.ts as the official way to skip server-side execution.
By default, the SDK requests https://agentdeskbot.vercel.app/widget.js. If you are using a self-hosted or white-labeled deployment, specify your custom absolute URL using the script-src prop:
<AgentDeskWidget
bot-id="YOUR_BOT_ID"
script-src="https://support.yourapp.com/widget.js"
/>If your page enforces a strict CSP, inline scripts or dynamically injected scripts might be blocked. You can propagate a CSP cryptographic nonce directly to the injected script tag using the csp-nonce prop:
<AgentDeskWidget
bot-id="YOUR_BOT_ID"
csp-nonce="random_cryptographic_nonce_string"
/>This nonce is applied to the injected <script> tag and passed down to dynamically loaded styles.
If the widget loads but displays a loading/connection error, check the browser console network tab. By default, it requests {apiOrigin}/api/widget/config/{botId}. You can override this endpoint entirely using the config-url prop:
<AgentDeskWidget
bot-id="YOUR_BOT_ID"
config-url="https://api.mysupport.com/custom/widget-config"
/>For security, the message listener validates event.origin against the page origin, api-origin prop, and script-src prop.
If you are running the widget backend on api.support.com but embedding it on app.com, ensure you pass the correct api-origin and script-src props:
<AgentDeskWidget
bot-id="YOUR_BOT_ID"
api-origin="https://api.support.com"
script-src="https://api.support.com/widget.js"
/>Vue's <Suspense> and HMR can re-mount components. The SDK is designed to handle this — it deduplicates scripts by botId. If you still see two widgets, make sure you aren't rendering two different <AgentDeskWidget> components with the same botId on the page.
This package requires Vue 3. For Vue 2, use the manual script-tag approach — the underlying widget.js IIFE is framework-agnostic.
Migrating to v0.1.0
Breaking change (v0.1.0): The widget's
postMessagetarget origin is now strictlywindow.location.origin(previously*). If you were listening foragentdesk-widget-open/agentdesk-widget-closeevents directly viawindow.addEventListener('message', …), your listener must be on the same origin as the page mounting the widget, or it will silently drop events.
The official React and Vue SDKs handle this for you — no action is required unless you wrote a custom adapter.
Related Packages
| Package | Description |
| --- | --- |
| @agentdeskbot/core | Shared TypeScript types (used internally). |
| @agentdeskbot/react | React & Next.js SDK for the same widget. |
License
MIT © AgentDesk
