React UI Components

Pre-built, themeable React components for voice agent interfaces — conversation views, audio-reactive visualizers, and control buttons.

These components require an AgentProvider ancestor. See React Hooks for provider setup. For the core JavaScript SDK, see JavaScript.

Installation

$npm install @deepgram/ui

Import the stylesheet in your app’s entry point:

1import "@deepgram/ui/styles.css";

@deepgram/ui re-exports all hooks from @deepgram/react and all types from @deepgram/agents. You can import everything from a single package.

Live Preview

The embedded widget below uses the components documented on this page — conversation panel, start button, microphone toggle, speaker toggle, text input, and the orb visualizer.

Usage

A complete voice agent interface in under 30 lines:

1import {
2 AgentProvider,
3 AgentConversation,
4 AgentTextInput,
5 AgentStartButton,
6 AgentMicrophoneButton,
7 AgentSpeakerButton,
8 Orb,
9} from "@deepgram/ui";
10import "@deepgram/ui/styles.css";
11
12function App() {
13 return (
14 <AgentProvider
15 config={{
16 auth: { tokenFactory: () => fetch("/api/token").then((r) => r.text()) },
17 agent: "YOUR_AGENT_ID",
18 }}
19 >
20 <div data-dg-agent>
21 <Orb size={120} />
22 <AgentConversation />
23 <AgentTextInput />
24 <div>
25 <AgentStartButton />
26 <AgentMicrophoneButton />
27 <AgentSpeakerButton />
28 </div>
29 </div>
30 </AgentProvider>
31 );
32}

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

Every component is optional. Use one or all, and mix them with your own components inside the provider.

Display Components

AgentStatus

Renders the current connection state as a text label. Updates automatically as the session connects, disconnects, or reconnects.

Connected

1<AgentStatus />

Props:

PropTypeDefaultDescription
classNamestringAdditional CSS class.
labelsPartial<Record<string, string>>See belowOverride the display text for each state.

Default labels: "Not started", "Connecting...", "Connected", "Reconnecting...", "Disconnected".

1<AgentStatus
2 labels={{
3 idle: "Ready",
4 connecting: "Connecting...",
5 connected: "Live",
6 disconnected: "Offline",
7 }}
8/>

Data attributes: data-agent-status, data-state (current state value).

AgentConversation

Scrollable conversation history showing user and agent messages.

What time is my next meeting?

You have a 1:1 with Sarah at 3:30 PM.

1<AgentConversation />

Props:

PropTypeDefaultDescription
classNamestringCSS class for the container.
itemClassNamestringCSS class applied to each message.
renderMessage(entry: ConversationEntry) => ReactNodeCustom render function for messages.
emptyStateReactNodeContent shown when conversation is empty.
autoScrollbooleantrueScroll to latest message automatically.
1<AgentConversation
2 emptyState={<p>Say something to start the conversation.</p>}
3 renderMessage={(entry) => (
4 <div className={entry.role === "user" ? "user-msg" : "agent-msg"}>
5 {entry.content}
6 </div>
7 )}
8/>

Data attributes: data-agent-conversation on the container, data-role="user" or data-role="assistant" on each message.

Response

Lightweight markdown renderer for agent text. Handles bold, italic, inline code, code blocks, lists, headings, links, and horizontal rules. Supports streaming — update the children string as tokens arrive.

Voice agents combine speech-to-text, an LLM, and text-to-speech in a single connection.

  • Low-latency conversation
  • Natural prosody
1<Response>{markdownString}</Response>

Props:

PropTypeDefaultDescription
childrenstringMarkdown string to render.
classNamestringAdditional CSS class.

Data attributes: data-agent-response.

Input Components

AgentTextInput

Text input field for sending messages to the agent. Submits on Enter (Shift+Enter for newline).

1<AgentTextInput />

Props:

PropTypeDefaultDescription
classNamestringAdditional CSS class.
placeholderstring"Type a message..."Input placeholder text.
disabledbooleanfalseDisable the input.
onSend(text: string) => voidCallback when a message is sent.
submitButtonReactNodeCustom send button element.

Data attributes: data-agent-text-input.

Control Components

AgentStartButton

Connect/disconnect toggle button. Reflects the current session state automatically.

1<AgentStartButton />

Props:

PropTypeDefaultDescription
classNamestringAdditional CSS class.
startLabelReactNode"Start"Label when idle.
connectingLabelReactNode"Connecting..."Label while connecting.
stopLabelReactNode"Stop"Label when connected.
reconnectingLabelReactNode"Reconnecting..."Label while reconnecting.
onClick() => voidOptional click handler override.

Data attributes: data-agent-start-button, data-state (current state value).

AgentMicrophoneButton

Microphone mute/unmute toggle. Renders SVG mic icons by default.

1<AgentMicrophoneButton />

Props:

PropTypeDefaultDescription
classNamestringAdditional CSS class.
activeLabelReactNodeMic iconContent when microphone is active.
mutedLabelReactNodeMic-off iconContent when muted.
disabledLabelReactNodeContent when microphone is unavailable. Returns null if omitted.
onClick() => voidOptional click handler override.

Data attributes: data-agent-mic-button, data-state ("active", "muted", "inactive", or "disabled").

AgentSpeakerButton

Speaker mute/unmute toggle. Renders SVG speaker icons by default.

1<AgentSpeakerButton />

Props:

PropTypeDefaultDescription
classNamestringAdditional CSS class.
activeLabelReactNodeSpeaker iconContent when speaker is active.
mutedLabelReactNodeSpeaker-off iconContent when muted.
onClick() => voidOptional click handler override.

Data attributes: data-agent-speaker-button, data-state ("active" or "muted").

VoiceButton

All-in-one button that combines connection and mode state into a single control. The appearance changes across five lifecycle states: idle, connecting, listening, speaking, and error.

1<VoiceButton />

Props:

PropTypeDefaultDescription
classNamestringAdditional CSS class.
labelsPartial<Record<VoiceButtonState, ReactNode>>See belowText for each state.
onClick() => voidOptional click handler override.

Default labels: "Start conversation", "Connecting...", "Listening...", "Agent speaking", "Error".

Style each state with the data-voice-state attribute:

1[data-voice-state="listening"] {
2 border-color: var(--dg-va-primary);
3}
4[data-voice-state="speaking"] {
5 background: var(--dg-va-primary);
6 animation: pulse 1.5s infinite;
7}

Data attributes: data-agent-voice-button, data-voice-state ("idle", "connecting", "listening", "speaking", "error").

Visualization Components

Orb

Deepgram’s animated hoop visualization. Canvas 2D rendering of four crescent arcs with gradient colors — lightweight and works everywhere without WebGL. Audio-reactive: the orb responds to actual microphone input and agent playback volume in real time.

Three visual states:

  • idle — deflated crescent, slow rocking, minimal animation
  • listening — full circle, gentle pulse, mic-reactive radius flutter
  • talking — crescent mouth, fast rotation, volume-modulated mouth movement
1<Orb size={200} />

Props:

PropTypeDefaultDescription
sizenumber200Diameter in pixels.
colors[string, string]Deepgram greensTwo gradient colors.
state"idle" | "listening" | "talking""idle"Visual state.
getInputVolume() => numberGetter sampled per frame for mic volume (0—1).
getOutputVolume() => numberGetter sampled per frame for output volume (0—1).
inputVolumenumberDirect mic volume value (0—1) for manual control.
outputVolumenumberDirect output volume value (0—1) for manual control.
classNamestringAdditional CSS class.

Automatic mode (default inside AgentProvider):

1<Orb />

The orb reads getInputVolume() and getOutputVolume() every animation frame with zero re-renders.

Manual mode — push volume values directly:

1<Orb inputVolume={0.5} outputVolume={0.3} state="talking" />

Custom volume sources:

1<Orb
2 getInputVolume={myMicAnalyser}
3 getOutputVolume={myPlayerAnalyser}
4/>

Custom colors:

1<Orb colors={["#6366f1", "#ec4899"]} />

Data attributes: data-agent-orb, data-orb-state ("idle", "listening", "talking").

BarVisualizer

Real-time frequency bar visualization. Renders vertical bars on a canvas that react to audio input or output.

1<BarVisualizer source="output" barCount={16} />

Props:

PropTypeDefaultDescription
source"input" | "output""output"Microphone or agent audio.
barCountnumber16Number of frequency bars.
classNamestringAdditional CSS class.

Data attributes: data-agent-bar-visualizer.

LiveWaveform

Smooth oscillating waveform driven by a volume source. Blends two sine waves for an organic feel.

1import { useAgentMicrophone } from "@deepgram/ui";
2
3function MyWaveform() {
4 const { getInputVolume } = useAgentMicrophone();
5 return <LiveWaveform getVolume={getInputVolume} />;
6}

Props:

PropTypeDefaultDescription
getVolume(() => number) | (() => number)[]Volume source(s) returning 0—1. When multiple are provided, the max value is used per frame.
activebooleantrueWhether the waveform animates. Renders a flat line when false.
colorstring--dg-va-primaryLine color.
lineWidthnumber2Stroke width in pixels.
classNamestringAdditional CSS class.

Data attributes: data-agent-live-waveform.

Utility Components

MicSelector

Dropdown for selecting the audio input device. Enumerates available microphones, requests permission on first open, and updates automatically when devices are plugged in or removed.

1const [deviceId, setDeviceId] = useState("");
2<MicSelector value={deviceId} onValueChange={setDeviceId} />

Props:

PropTypeDefaultDescription
valuestringCurrently selected device ID (controlled).
onValueChange(deviceId: string) => voidCallback when the user selects a device.
classNamestringAdditional CSS class.
disabledbooleanfalseDisable the selector.

Data attributes: data-agent-mic-selector.

Theming

All components use CSS custom properties scoped to [data-dg-agent]. Add this attribute to your container element to apply the theme. Because these are standard CSS custom properties, they work with any CSS framework — Tailwind, CSS Modules, or plain stylesheets.

1<div data-dg-agent>
2 <AgentConversation />
3 <AgentTextInput />
4</div>

Design tokens

Tokens follow the shadcn --color-* naming convention generated by Tailwind v4’s @theme. The package ships sensible light defaults; dark values are applied automatically when [data-dg-scheme="dark"] is set or when the user’s system prefers dark mode. Override any token on a [data-dg-agent] ancestor to retheme.

1[data-dg-agent] {
2 /* Brand */
3 --color-primary: #13ef93;
4 --color-primary-foreground: #000000;
5
6 /* Surfaces */
7 --color-background: #ffffff;
8 --color-foreground: #111827;
9 --color-card: #f3f4f6;
10 --color-card-foreground: #111827;
11 --color-popover: #ffffff;
12 --color-popover-foreground: #111827;
13 --color-muted: #f3f4f6;
14 --color-muted-foreground: #6b7280;
15 --color-accent: #f9fafb;
16 --color-accent-foreground: #111827;
17 --color-input: #f3f4f6;
18 --color-border: rgba(0, 0, 0, 0.1);
19 --color-ring: #13ef93;
20 --color-secondary: #f3f4f6;
21 --color-secondary-foreground: #111827;
22 --color-destructive: #dc2626;
23 --color-destructive-foreground:#ffffff;
24
25 /* Typography & shape */
26 --font-sans: system-ui, -apple-system, sans-serif;
27 --radius: 1rem;
28
29 /* Widget layout (panel + FAB sizing) */
30 --dg-va-panel-w: min(440px, 100vw);
31 --dg-va-fab-size: 56px;
32 --dg-va-padding: 16px;
33
34 /* Derived from --color-primary by default — override only if you need a different relationship */
35 --primary-hover: color-mix(in srgb, var(--color-primary) 85%, #000);
36 --primary-active: color-mix(in srgb, var(--color-primary) 70%, #000);
37 --msg-user-bg: color-mix(in srgb, var(--color-primary) 12%, transparent);
38 --msg-user-border: color-mix(in srgb, var(--color-primary) 30%, transparent);
39}

Color scheme

Light/dark switching is driven by the data-dg-scheme attribute on the same element that has data-dg-agent. Without an explicit value, the components follow the user’s prefers-color-scheme.

1<div data-dg-agent data-dg-scheme="dark">
2 {/* Always renders in dark mode */}
3</div>
BehaviourSelector
Force dark[data-dg-agent][data-dg-scheme="dark"]
Force light[data-dg-agent][data-dg-scheme="light"]
Follow system (default)no attribute, prefers-color-scheme: dark triggers dark

If your app uses Tailwind’s dark: variant or next-themes, write a small effect that mirrors that state onto data-dg-scheme. The package does not infer it from a .dark ancestor class.

Custom theme example

A teal-on-midnight palette called Aurora, applied entirely through CSS custom properties on the host element. Same components, completely different feel.

1[data-dg-agent].aurora-theme {
2 /* Brand */
3 --color-primary: #5eead4;
4 --color-primary-foreground: #042f2e;
5 --color-ring: #5eead4;
6
7 /* Surfaces */
8 --color-background: #0a0e1a;
9 --color-foreground: #e6edf6;
10 --color-card: #121829;
11 --color-card-foreground: #e6edf6;
12 --color-popover: #121829;
13 --color-popover-foreground: #e6edf6;
14 --color-muted: #1a2236;
15 --color-muted-foreground: #94a3b8;
16 --color-accent: #182238;
17 --color-accent-foreground: #5eead4;
18 --color-input: #0d1322;
19 --color-border: rgba(94, 234, 212, 0.14);
20 --color-secondary: #1a2236;
21 --color-secondary-foreground: #5eead4;
22
23 /* Derived (override the color-mix defaults for a softer glow) */
24 --primary-hover: #2dd4bf;
25 --primary-active: #14b8a6;
26 --msg-user-bg: rgba(94, 234, 212, 0.10);
27 --msg-user-border: rgba(94, 234, 212, 0.28);
28
29 /* Slightly tighter corners than the default 1rem */
30 --radius: 14px;
31}

Apply the class to your [data-dg-agent] container (or extend the override to the element itself) and every component inside picks up the new palette. The same pattern works for any palette — swap the values, keep the keys.

Styling with Data Attributes

Components use data-agent-* attribute selectors instead of class names. This prevents collisions with your application’s CSS framework — no specificity battles with Tailwind utilities or CSS Modules hashes.

1/* Target the conversation container */
2[data-agent-conversation] {
3 max-height: 400px;
4}
5
6/* Target user messages */
7[data-agent-conversation] [data-role="user"] {
8 text-align: right;
9}
10
11/* Target agent messages */
12[data-agent-conversation] [data-role="assistant"] {
13 font-style: italic;
14}
15
16/* Target the text input */
17[data-agent-text-input] {
18 font-size: 16px;
19}
20
21/* Target the orb by state */
22[data-agent-orb][data-orb-state="talking"] {
23 filter: brightness(1.2);
24}

Data Attribute Reference

ComponentAttributeValues
Containerdata-dg-agent
Color schemedata-dg-scheme"light", "dark"
AgentStatusdata-agent-status, data-state"idle", "connecting", "connected", "reconnecting", "disconnected"
AgentConversationdata-agent-conversation
Messagesdata-role"user", "assistant"
AgentTextInputdata-agent-text-input
AgentStartButtondata-agent-start-button, data-state"idle", "connecting", "connected", "reconnecting", "disconnected"
AgentMicrophoneButtondata-agent-mic-button, data-state"active", "muted", "inactive", "disabled"
AgentSpeakerButtondata-agent-speaker-button, data-state"active", "muted"
VoiceButtondata-agent-voice-button, data-voice-state"idle", "connecting", "listening", "speaking", "error"
Orbdata-agent-orb, data-orb-state"idle", "listening", "talking"
BarVisualizerdata-agent-bar-visualizer
LiveWaveformdata-agent-live-waveform
MicSelectordata-agent-mic-selector
Responsedata-agent-response