Widgets

Drop-in floating UI components for chat and voice. Zero custom UI code needed — just configure and go.

When to use widgets vs. custom UI

Widgets are pre-built floating UIs (chat bubble, voice button) — great for quickly adding AI to any page. If you need full control over the design, use the XpectrumChat / XpectrumVoice classes directly and build your own UI.

1. ChatWidget

A floating chat bubble that opens into a full chat window. Uses Shadow DOM for style isolation — it won't conflict with your existing CSS.

Basic usage

typescript
import { ChatWidget } from '@xpectrum/sdk';

const widget = new ChatWidget({
  apiKey: 'app-xxxxxxxxxxxx',
  baseUrl: 'https://app.yourserver.com/api/v1',
});

// Control programmatically
widget.open();
widget.close();
widget.toggle();
widget.destroy(); // Remove completely

All config options

PropertyTypeRequiredDefaultDescription
apiKeystringYesBearer token for chat API.
baseUrlstringYesChat API endpoint URL.
containerHTMLElementNodocument.bodyDOM element to mount the widget into.
position'bottom-right' | 'bottom-left'No'bottom-right'Where the floating button appears.
buttonColorstringNo#7C3AEDHex color for the floating button.
buttonSizenumberNo48Button diameter in pixels.
theme'light' | 'dark' | 'auto'No'light'Color theme for the chat window.
zIndexnumberNo2147483647CSS z-index for the widget.
welcomeMessagestringNoInitial message shown when chat opens.
userstringNoUnique user identifier for conversation history.
windowWidthnumberNo400Chat window width in pixels.
windowHeightnumberNo600Chat window height in pixels.

Customized example

typescript
const widget = new ChatWidget({
  apiKey: 'app-xxxxxxxxxxxx',
  baseUrl: 'https://app.yourserver.com/api/v1',
  position: 'bottom-left',
  buttonColor: '#10b981',
  buttonSize: 56,
  theme: 'dark',
  welcomeMessage: 'Hi there! How can I help you today?',
  user: 'user-123',
  windowWidth: 450,
  windowHeight: 650,
});

Features

  • Shadow DOM for complete style isolation
  • Auto-resizing textarea for message input
  • Real-time message streaming with typing indicator
  • Conversation persistence across sessions
  • ESC key to close, Enter to send (Shift+Enter for newline)
  • Loads app parameters (opening statement, suggested questions) on first open

2. VoiceWidget

A floating button that opens a voice call interface with live transcription.

Basic usage

typescript
import { VoiceWidget } from '@xpectrum/sdk';

const widget = new VoiceWidget({
  apiKey: 'xpectrum_ai_sk_...',
  agentName: '6b00c03f-...',
  baseUrl: 'https://api.xpectrum.co/',
});

widget.open();
widget.close();
widget.toggle();
widget.destroy();

All config options

PropertyTypeRequiredDefaultDescription
apiKeystringYesAPI key for voice auth (x-api-key header).
agentNamestringYesAgent UUID or name to connect to.
baseUrlstringYesVoice server URL.
containerHTMLElementNodocument.bodyDOM element to mount into.
position'bottom-right' | 'bottom-left'No'bottom-right'Floating button position.
buttonColorstringNo#7C3AEDHex color for the button.
buttonSizenumberNo48Button diameter in pixels.
zIndexnumberNo2147483647CSS z-index.
windowWidthnumberNo360Voice window width in pixels.
windowHeightnumberNo480Voice window height in pixels.
onTranscription(segment) => voidNoExternal callback for transcription events.
onStateChange(state) => voidNoExternal callback for connection state changes.

Features

  • Start/hang up call buttons
  • Mute/unmute microphone button
  • Real-time transcription display (last 50 segments)
  • Color-coded connection status (gray=disconnected, amber=connecting, green=connected, red=failed)
  • Auto-disconnect when widget closes

3. OmnichannelWidget

A single floating button that offers both Chat and Voice options in a menu. Manages both widgets internally.

typescript
import { OmnichannelWidget } from '@xpectrum/sdk';

const widget = new OmnichannelWidget({
  // Chat config
  chatBaseUrl: 'https://app.yourserver.com/api/v1',
  chatApiKey: 'app-xxxxxxxxxxxx',

  // Voice config
  voiceBaseUrl: 'https://api.xpectrum.co/',
  apiKey: 'xpectrum_ai_sk_...',
  agentName: '6b00c03f-...',

  // Shared options
  position: 'bottom-right',
  buttonColor: '#7C3AED',
  user: 'user-123',
});

// Programmatic control
widget.openChat();
widget.openVoice();
widget.close();
widget.destroy();
PropertyTypeRequiredDefaultDescription
chatBaseUrlstringYesChat API endpoint URL.
chatApiKeystringYesChat API key (Bearer token).
voiceBaseUrlstringYesVoice server URL.
apiKeystringYesVoice API key (x-api-key).
agentNamestringYesVoice agent UUID.
containerHTMLElementNodocument.bodyMount point.
position'bottom-right' | 'bottom-left'No'bottom-right'Button position.
buttonColorstringNoHex color for the main button.
zIndexnumberNoCSS z-index.
userstringNoUser ID for chat history.
chatPartial<ChatWidgetConfig>NoOverride any ChatWidget config.
voicePartial<VoiceWidgetConfig>NoOverride any VoiceWidget config.

4. Embed via Script Tag (No Build Step)

For plain HTML pages, you can add widgets with just a script tag — no npm install or build step needed.

Chat embed

html
<script>
  window.XpectrumChatConfig = {
    apiKey: 'app-xxxxxxxxxxxx',
    baseUrl: 'https://app.yourserver.com/api/v1',
    position: 'bottom-right',
    buttonColor: '#7C3AED',
    theme: 'light',
    welcomeMessage: 'Hi! How can I help?',
  };
</script>
<script src="https://unpkg.com/@xpectrum/sdk/dist/chat-embed.min.js" defer></script>

Voice embed

html
<script>
  window.XpectrumVoiceConfig = {
    token: 'xpectrum_ai_sk_...',
    baseUrl: 'https://api.xpectrum.co/',
    position: 'bottom-right',
    buttonColor: '#7C3AED',
  };
</script>
<script src="https://unpkg.com/@xpectrum/sdk/dist/voice-embed.min.js" defer></script>

5. Using Widgets in React / Next.js

Client component required

Widgets manipulate the DOM, so they must run in a client component. Add 'use client' at the top of your file in Next.js App Router.
components/ChatBubble.tsx
'use client';

import { useEffect, useRef } from 'react';
import { ChatWidget } from '@xpectrum/sdk';

export default function ChatBubble() {
  const widgetRef = useRef<ChatWidget | null>(null);

  useEffect(() => {
    widgetRef.current = new ChatWidget({
      apiKey: process.env.NEXT_PUBLIC_CHAT_API_KEY!,
      baseUrl: process.env.NEXT_PUBLIC_CHAT_BASE_URL!,
      position: 'bottom-right',
      buttonColor: '#7C3AED',
      theme: 'auto',
      welcomeMessage: 'Hello! How can I help you today?',
    });

    return () => widgetRef.current?.destroy();
  }, []);

  return null; // Widget manages its own DOM
}

Then use it in any page or layout:

app/layout.tsx
import ChatBubble from './components/ChatBubble';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <ChatBubble />
      </body>
    </html>
  );
}