LiveKit voice UI components for React
LiveKit Agents handles the realtime media and agent session layer. orb-ui handles the visible React UI layer: an animated voice orb, audio-reactive feedback, and predictable states for the conversation.
The token-endpoint example below is the intended application setup. Sandbox, existing-room, raw
credential, and custom fetcher modes are advanced alternatives; you do not need to configure all of
them.
Install
Install orb-ui and the LiveKit client package used by your app:
npm install orb-ui livekit-client
Use a token endpoint
In the recommended setup, your app exposes a token endpoint and the adapter fetches fresh LiveKit
connection details when the orb starts. The endpoint should return { serverUrl, participantToken }.
import { Room, TokenSource, createAudioAnalyser } from 'livekit-client'
import { Orb } from 'orb-ui'
import { createLiveKitAdapter } from 'orb-ui/adapters'
const adapter = createLiveKitAdapter({
tokenSource: TokenSource.endpoint('/api/livekit-token'),
tokenOptions: {
agentName: 'your-agent-name',
roomName: () => `orb-${crypto.randomUUID()}`,
},
createAudioAnalyser,
RoomClass: Room,
})
export function LiveKitVoiceUI() {
return <Orb adapter={adapter} theme="circle" aria-label="Start LiveKit assistant" />
}
You can also provide your own fetcher:
const adapter = createLiveKitAdapter({
getConnectionDetails: async ({ agentName, roomName }) => {
const response = await fetch('/api/livekit-token', {
method: 'POST',
body: JSON.stringify({ agentName, roomName }),
})
return response.json()
},
tokenOptions: {
agentName: 'your-agent-name',
roomName: () => `orb-${crypto.randomUUID()}`,
},
createAudioAnalyser,
RoomClass: Room,
})
Use a LiveKit Cloud sandbox
For local and preview testing, LiveKit’s sandbox token server can generate connection details without
your own backend. Do not use this mode in production.
import { Room, TokenSource, createAudioAnalyser } from 'livekit-client'
import { Orb } from 'orb-ui'
import { createLiveKitAdapter } from 'orb-ui/adapters'
const adapter = createLiveKitAdapter({
tokenSource: TokenSource.sandboxTokenServer('token-server-abc123'),
tokenOptions: {
agentName: 'your-agent-name',
roomName: () => `orb-${crypto.randomUUID()}`,
},
createAudioAnalyser,
RoomClass: Room,
})
export function LiveKitVoiceUI() {
return <Orb adapter={adapter} theme="circle" aria-label="Start LiveKit assistant" />
}
Use an existing room
Use an existing room when your app already owns the LiveKit lifecycle. The adapter subscribes to
room state but does not expose start or stop.
import { createAudioAnalyser } from 'livekit-client'
import { Orb } from 'orb-ui'
import { createLiveKitAdapter } from 'orb-ui/adapters'
const adapter = createLiveKitAdapter({
room,
createAudioAnalyser,
})
export function LiveKitVoiceUI() {
return <Orb adapter={adapter} theme="circle" aria-label="LiveKit assistant status" />
}
Use raw connection details
Raw credentials are supported as an advanced escape hatch. Generate participant tokens on your
server, keep them short-lived, and avoid hard-coding them in browser bundles.
const adapter = createLiveKitAdapter({
serverUrl: 'wss://your-project.livekit.cloud',
participantToken: 'short-lived-participant-token',
createAudioAnalyser,
RoomClass: Room,
})
State mapping
The adapter reads the agent participant’s lk.agent.state attribute and maps LiveKit state into orb-ui state:
speaking -> speaking
thinking -> thinking
listening and idle -> listening
connecting, pre-connect-buffering, and initializing -> connecting
disconnected -> idle
failed -> error
LiveKit remote agent audio is attached when available. The adapter uses createAudioAnalyser for
both sides of the conversation: the local microphone emits normalized inputVolume while the agent
is listening, and remote agent audio emits normalized outputVolume while the agent is speaking.
The shared volume value follows whichever side is active so every orb-ui theme reacts correctly.
Token handling
Do not create LiveKit access tokens in the browser. Use TokenSource.endpoint, getConnectionDetails,
or another server-backed flow to generate short-lived participant tokens.
Controlled mode
If your app already normalizes LiveKit state and volume, pass those values directly as a signal.
<Orb signal={{ state: voiceState, inputVolume, outputVolume }} theme="circle" />
Last modified on July 10, 2026