Svelte integration guide
This guide covers patterns for integrating the Univerx Client SDK into Svelte applications, including reactive state with Svelte stores and SvelteKit SSR considerations.
Basic integration
Section titled “Basic integration”Use onMount to initialise the widget and onDestroy to clean up. Both lifecycle functions are guaranteed to run only in the browser.
<script lang="ts"> import { onMount, onDestroy } from 'svelte'; import { WidgetClient } from '@univerx/client-sdk';
let widget: WidgetClient | null = null;
onMount(() => { widget = new WidgetClient({ widgetKey: 'your-widget-key', onAgentAssigned: (agent) => console.log(`Agent: ${agent.name}`), onCallEnded: () => widget?.destroy(), });
widget.init(); });
onDestroy(() => { widget?.destroy(); widget = null; });</script>
<!-- SDK renders its own UI; no markup needed -->Reactive state with Svelte stores
Section titled “Reactive state with Svelte stores”Use Svelte’s built-in writable stores to expose widget state to your template and other components:
<script lang="ts"> import { onMount, onDestroy } from 'svelte'; import { writable } from 'svelte/store'; import { WidgetClient } from '@univerx/client-sdk'; import type { WidgetState, Agent } from '@univerx/client-sdk';
const state = writable<WidgetState>('idle'); const queuePosition = writable<number | null>(null); const currentAgent = writable<Agent | null>(null);
let widget: WidgetClient | null = null;
onMount(() => { widget = new WidgetClient({ widgetKey: 'your-widget-key', onReady: () => state.set('ready'), onCallStarted: () => state.set('in_call'), onCallEnded: () => state.set('ended'), });
widget.on('queue:update', (pos) => queuePosition.set(pos)); widget.on('queue:left', () => queuePosition.set(null)); widget.on('agent:assigned', (agent) => currentAgent.set(agent));
widget.init(); });
onDestroy(() => { widget?.destroy(); widget = null; });
async function joinQueue() { await widget?.joinQueue({ terms: true }); state.set('queued'); }
async function endCall() { await widget?.endSession(); }</script>
{#if $state === 'ready'} <button on:click={joinQueue}>Get support</button>{/if}
{#if $state === 'queued'} <p>Queue position: {$queuePosition}</p>{/if}
{#if $state === 'in_call'} <button on:click={endCall}>End call</button>{/if}Shared store module
Section titled “Shared store module”To share the widget instance across multiple components, export a store and init function from a dedicated module:
import { writable, get } from 'svelte/store';import { WidgetClient } from '@univerx/client-sdk';import type { WidgetConfig, WidgetState, Agent } from '@univerx/client-sdk';
export const widgetState = writable<WidgetState>('idle');export const queuePosition = writable<number | null>(null);export const currentAgent = writable<Agent | null>(null);
let instance: WidgetClient | null = null;
export function initWidget(config: WidgetConfig): void { if (instance) return; // already initialised
instance = new WidgetClient({ ...config, onReady: () => widgetState.set('ready'), onCallStarted: () => widgetState.set('in_call'), onCallEnded: () => widgetState.set('ended'), });
instance.on('queue:update', (pos) => queuePosition.set(pos)); instance.on('queue:left', () => queuePosition.set(null)); instance.on('agent:assigned', (agent) => currentAgent.set(agent));
instance.init();}
export function destroyWidget(): void { instance?.destroy(); instance = null;}
export function getWidget(): WidgetClient | null { return instance;}Use it in your root layout component:
<script lang="ts"> import { onMount, onDestroy } from 'svelte'; import { initWidget, destroyWidget } from '$lib/widget';
onMount(() => { initWidget({ widgetKey: 'your-widget-key' }); });
onDestroy(() => { destroyWidget(); });</script>
<slot />Cobrowsing consent
Section titled “Cobrowsing consent”Listen for cobrowse:consent:required and bind the handlers to local state to drive your consent UI:
<script lang="ts"> import { onMount, onDestroy } from 'svelte'; import { WidgetClient } from '@univerx/client-sdk';
let widget: WidgetClient | null = null; let consentHandlers: { accept: () => void; decline: () => void } | null = null;
onMount(() => { widget = new WidgetClient({ widgetKey: 'your-widget-key' });
widget.on('cobrowse:consent:required', (handlers) => { consentHandlers = handlers; });
widget.init(); });
onDestroy(() => { widget?.destroy(); });</script>
{#if consentHandlers} <div class="consent-dialog"> <p>The agent is requesting screen access. Do you allow cobrowsing?</p> <button on:click={() => { consentHandlers?.accept(); consentHandlers = null; }}> Allow </button> <button on:click={() => { consentHandlers?.decline(); consentHandlers = null; }}> Decline </button> </div>{/if}SvelteKit SSR
Section titled “SvelteKit SSR”SvelteKit renders pages on the server by default. Since onMount only runs in the browser, initialising inside it is already safe. However, if you reference the SDK at the top level of a +page.server.ts or in any server-only code, it will throw.
For dynamic imports in edge cases:
<script lang="ts"> import { onMount } from 'svelte'; import type { WidgetClient } from '@univerx/client-sdk';
let widget: WidgetClient | null = null;
onMount(async () => { // Dynamically import to ensure the module is never evaluated on the server const { WidgetClient } = await import('@univerx/client-sdk'); widget = new WidgetClient({ widgetKey: 'your-widget-key' }); widget.init(); });</script>