Widget Embedding Guide

Add a voice agent to any website with a single script tag. Six layout modes, full theming, voice activity detection, and zero build step required.

Add a voice agent to any website. No framework required, no build step needed. The widget ships as a self-contained bundle with its own Preact runtime (~160KB gzipped), six layout modes, full design-token theming, and built-in voice activity detection. It works from a CDN or as an ES module, and tears down cleanly for single-page apps.

The widget bundles everything internally. No React or build tooling required. For React-native integration, see React UI Components.

Quick Start

Install

$npm install @deepgram/agents-widget

ES Module

1import { init } from "@deepgram/agents-widget";
2
3const teardown = init({
4 tokenFactory: () => fetch("/api/deepgram-token").then((r) => r.text()),
5 agent: "YOUR_AGENT_ID",
6 layout: "sidebar",
7});
8
9// Call teardown() to unmount the widget and clean up

Replace YOUR_AGENT_ID with a Reusable Agent Configuration UUID, or pass an inline agent config object instead. See Agent Configuration for both patterns.

CDN

Load the widget from cdn.deepgram.com for a no-build path:

1<script src="https://cdn.deepgram.com/widgets/latest/widget.umd.js"></script>
2<script>
3 const teardown = DeepgramAgent.init({
4 tokenFactory: () => fetch("/api/deepgram-token").then((r) => r.text()),
5 agent: "YOUR_AGENT_ID",
6 });
7</script>

The latest segment in the URL above is replaced with the current pinned version when this page loads, so the snippet you copy targets a specific build, not a moving release pointer.

Self-hosted UMD

The package ships a UMD bundle at dist/widget.umd.js for <script>-tag usage. Copy or symlink it from node_modules/@deepgram/agents-widget/dist/widget.umd.js into your static assets, then load it like any other script:

1<script src="/assets/widget.umd.js"></script>
2<script>
3 const teardown = DeepgramAgent.init({
4 tokenFactory: () => fetch("/api/deepgram-token").then((r) => r.text()),
5 agent: "YOUR_AGENT_ID",
6 });
7</script>

Never include your API key in client-side code. Use tokenFactory to fetch short-lived tokens from your server. The apiKey option exists only for local development.

Layouts

The widget ships with six layout modes. Set the layout option to choose one.

A panel that slides in from the edge of the screen. Toggled by a floating action button (FAB).

1init({
2 tokenFactory,
3 agent: "YOUR_AGENT_ID",
4 layout: "sidebar",
5 placement: "bottom-right",
6 defaultOpen: false,
7 dismissible: true,
8});

floating

A FAB button that reveals a floating overlay panel.

1init({
2 tokenFactory,
3 agent: "YOUR_AGENT_ID",
4 layout: "floating",
5 placement: "bottom-right",
6});

inline

Mounts directly into an existing DOM element. No FAB, no overlay.

1<div id="agent-container"></div>
1init({
2 tokenFactory,
3 agent: "YOUR_AGENT_ID",
4 layout: "inline",
5 containerId: "agent-container",
6});

embedded

Full-width card with configurable aspect ratio. Includes the conversation transcript. Ideal for landing pages and product demos.

1<div id="agent-embed"></div>
1init({
2 tokenFactory,
3 agent: "YOUR_AGENT_ID",
4 layout: "embedded",
5 containerId: "agent-embed",
6 theme: {
7 aspect: "16 / 9",
8 minHeight: "400px",
9 },
10});

button

A single talk button — press to start, press again to stop. Minimal footprint.

1init({
2 tokenFactory,
3 agent: "YOUR_AGENT_ID",
4 layout: "button",
5 placement: "bottom-right",
6});

orb

The Deepgram animated hoop visualization with start/stop controls. Audio-reactive — the orb responds to input and output volume in real time.

1init({
2 tokenFactory,
3 agent: "YOUR_AGENT_ID",
4 layout: "orb",
5 placement: "bottom-right",
6});

Placement

For layouts with a FAB (sidebar, floating, button, orb), set where the button appears:

1placement: "bottom-right" // default
2// Options: "bottom-right", "bottom-left", "bottom",
3// "top-right", "top-left", "top"

External Trigger Button

To use your own button instead of the built-in FAB, pass its element ID:

1<button id="my-agent-btn">Talk to AI</button>
1init({
2 tokenFactory,
3 agent: "YOUR_AGENT_ID",
4 layout: "sidebar",
5 buttonId: "my-agent-btn",
6});

To toggle the widget programmatically from anywhere:

1document.dispatchEvent(new Event("dg-agent-toggle"));

Features

Toggle UI features on or off:

1init({
2 tokenFactory,
3 agent: "YOUR_AGENT_ID",
4 showTranscript: true, // conversation history (default: true)
5 showMicToggle: true, // microphone mute button (default: true)
6 showSpeakerToggle: true, // speaker mute button (default: true)
7 showTextInput: true, // text input field (default: true)
8 vad: true, // voice activity detection (default: false)
9});

VAD Configuration

Enable Silero VAD to gate audio so only speech frames reach the agent. Pass true for defaults, or fine-tune the thresholds:

1init({
2 tokenFactory,
3 agent: "YOUR_AGENT_ID",
4 vad: {
5 speechThreshold: 0.5,
6 silenceThreshold: 0.35,
7 },
8});

When VAD is enabled, the microphone captures continuously but transmits only when speech is detected. This reduces bandwidth and improves turn-taking accuracy.

Text Customization

Override labels and placeholder text:

1init({
2 tokenFactory,
3 agent: "YOUR_AGENT_ID",
4 text: {
5 name: "Aria",
6 startLabel: "Talk to Aria",
7 stopLabel: "End conversation",
8 connectingLabel: "Connecting...",
9 inputPlaceholder: "Type a message...",
10 emptyStateHint: "Press start to begin talking.",
11 },
12});

Agent Overrides

Override the agent’s system prompt or greeting for this session without changing the agent configuration in the Deepgram console:

1init({
2 tokenFactory,
3 agent: "YOUR_AGENT_ID",
4 overrides: {
5 systemPrompt: "You are a customer support agent for Acme Corp.",
6 greeting: "Hi! How can I help you with your Acme account?",
7 },
8});

Callbacks

Listen to agent lifecycle events for analytics, logging, or UI integration:

1init({
2 tokenFactory,
3 agent: "YOUR_AGENT_ID",
4 on: {
5 onConnect: () => console.log("Connected"),
6 onDisconnect: (reason) => console.log("Disconnected:", reason),
7 onError: (err) => console.error("Error:", err),
8 onMessage: (msg) => console.log(`${msg.role}: ${msg.content}`),
9 onAgentStartedSpeaking: (msg) => console.log("Agent speaking"),
10 onFunctionCallRequest: (msg) => console.log("Function call:", msg),
11 onAgentError: (msg) => console.error("Agent error:", msg),
12 onReconnecting: (attempt, delayMs) =>
13 console.log(`Reconnecting (attempt ${attempt}, ${delayMs}ms)`),
14 },
15});
CallbackFires when
onConnectWebSocket connection opens
onDisconnectSession ends (user or server side)
onErrorSDK-level error occurs
onMessageAny conversation turn (user or assistant text)
onAgentStartedSpeakingAgent begins speaking; includes latency metrics
onFunctionCallRequestAgent requests a client-side function call
onAgentErrorAgent-reported error (distinct from SDK errors)
onReconnectingReconnect attempt starts; receives attempt number and delay

Color Scheme

Control how the widget adapts to light and dark mode:

1// Automatic -- follows prefers-color-scheme (default)
2colorScheme: "auto"
3
4// Force light or dark
5colorScheme: "light"
6colorScheme: "dark"
7
8// Class-based -- for CSS framework integration (e.g., Tailwind dark mode)
9colorScheme: {
10 mode: "class",
11 darkSelector: ".dark", // default
12 lightSelector: ".light", // default
13}

The class-based option watches for a CSS selector on any ancestor element. Use it when the host app controls theme via a class on <html> rather than OS preference.

Theming

Customize the widget’s appearance by overriding design tokens. Each property maps to a CSS custom property on the widget root element ([data-dg-agent]). Set a token here to override the built-in adaptive default in both light and dark modes.

1init({
2 tokenFactory,
3 agent: "YOUR_AGENT_ID",
4 theme: {
5 // Accent
6 primary: "#6366f1",
7 primaryHover: "#4f46e5",
8 primaryActive: "#4338ca",
9 onPrimary: "#ffffff",
10
11 // Surface
12 background: "#ffffff",
13 backgroundRaised: "#f9fafb",
14 backgroundInput: "#ffffff",
15 backgroundHover: "#f3f4f6",
16 backgroundActive: "#e5e7eb",
17
18 // Text
19 text: "#111827",
20 textMuted: "#6b7280",
21
22 // Chrome
23 border: "#e5e7eb",
24 error: "#ef4444",
25 overlay: "rgba(0, 0, 0, 0.25)",
26
27 // Messages
28 userMessageBackground: "#f3f4f6",
29 userMessageBorder: "#e5e7eb",
30
31 // Radius
32 panelRadius: "16px",
33 buttonRadius: "9999px",
34 inputRadius: "8px",
35 messageRadius: "12px",
36
37 // Structural
38 fabSize: 56,
39 padding: "16px",
40 font: "Inter, system-ui, sans-serif",
41 },
42});

To override only one color scheme, skip the theme option and write CSS directly:

1@media (prefers-color-scheme: dark) {
2 [data-dg-agent] {
3 --dg-va-bg: #0d1117;
4 }
5}

Embedded Layout Tokens

The embedded layout supports additional sizing tokens:

1theme: {
2 aspect: "4 / 3", // CSS aspect-ratio (default: "4 / 3")
3 minHeight: "320px", // default: "320px"
4 maxHeight: "80vh", // default: "80vh"
5}

Full Configuration Reference

1init({
2 // -- Auth (one required) --
3 apiKey: "...", // Development only
4 tokenFactory: () => Promise<string>, // Production
5
6 // -- Agent --
7 agent: "AGENT_ID" | AgentSettingsObject, // Required
8 overrides: { systemPrompt, greeting },
9
10 // -- Layout --
11 layout: "sidebar", // sidebar | inline | floating
12 // button | embedded | orb
13 placement: "bottom-right", // FAB position
14 containerId: "my-element", // Required for inline / embedded
15 buttonId: "my-button", // External trigger element
16 defaultOpen: false, // Start panel open (sidebar/floating)
17 dismissible: true, // Allow close/dismiss
18
19 // -- Features --
20 vad: false | { speechThreshold, silenceThreshold },
21 showTranscript: true,
22 showMicToggle: true,
23 showSpeakerToggle: true,
24 showTextInput: true,
25
26 // -- Text --
27 text: {
28 name, startLabel, stopLabel,
29 connectingLabel, inputPlaceholder, emptyStateHint,
30 },
31
32 // -- Theming --
33 colorScheme: "auto" | "light" | "dark"
34 | { mode: "class", darkSelector, lightSelector },
35 theme: { /* design tokens listed above */ },
36
37 // -- Callbacks --
38 on: {
39 onConnect, onDisconnect, onError, onMessage,
40 onAgentStartedSpeaking, onFunctionCallRequest,
41 onAgentError, onReconnecting,
42 },
43
44 // -- Audio --
45 playerSampleRate: 24_000, // Agent audio sample rate
46
47 // -- Network --
48 url: "wss://...", // Custom WebSocket URL (proxy)
49});

Cleanup

The init() function returns a teardown function. Call it to unmount the widget, remove all injected styles, and release audio resources. This is essential for single-page apps where the widget mounts and unmounts as the user navigates.

1const teardown = init({
2 tokenFactory,
3 agent: "YOUR_AGENT_ID",
4 layout: "sidebar",
5});
6
7// When the user navigates away or you no longer need the widget:
8teardown();

For frameworks with lifecycle hooks, call teardown in the cleanup phase:

1// React useEffect
2useEffect(() => {
3 const teardown = init({ tokenFactory, agent: "YOUR_AGENT_ID" });
4 return teardown;
5}, []);
6
7// Vue onUnmounted
8onMounted(() => {
9 const teardown = init({ tokenFactory, agent: "YOUR_AGENT_ID" });
10 onUnmounted(teardown);
11});