> ## Documentation Index
> Fetch the complete documentation index at: https://orb-ui.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# ElevenLabs Voice Orb UI for React

> Build an ElevenLabs Conversational AI voice orb UI in React with provider state mapping, audio-reactive themes, and accessible controls.

ElevenLabs Conversational AI can power realtime voice experiences. orb-ui turns those sessions into
a React voice orb UI with visible listening and speaking states, audio-reactive feedback, and
accessible call controls.

## Install

Install both orb-ui and the ElevenLabs client package used by the adapter:

```bash theme={null}
npm install orb-ui @elevenlabs/client
```

## Use the adapter

```tsx theme={null}
import { Conversation } from '@elevenlabs/client'
import { Orb } from 'orb-ui'
import { createElevenLabsAdapter } from 'orb-ui/adapters'

const adapter = createElevenLabsAdapter(Conversation, {
  agentId: 'your-agent-id',
})

export function ElevenLabsVoiceUI() {
  return <Orb adapter={adapter} theme="bars" aria-label="Start ElevenLabs assistant" />
}
```

For private agents, pass the server-generated `signedUrl` or `conversationToken` option that your app already gives to `Conversation.startSession()`.

## Choose the session credential

The adapter accepts the same mutually exclusive session shapes that the ElevenLabs client uses:

* `agentId` for a public agent, with an optional `connectionType`
* `signedUrl` for a private WebSocket session
* `conversationToken` for a private WebRTC session

Create private session credentials on your server. Do not expose a standard provider API key in
browser code. The adapter passes other supported `startSession` options through to the client, but
text-only sessions are intentionally outside orb-ui's voice UI contract.

```tsx theme={null}
const privateAdapter = createElevenLabsAdapter(Conversation, {
  conversationToken: await getConversationToken(),
  connectionType: 'webrtc',
})
```

Create the adapter with a fresh credential when that credential is single-use or expires. Your
application remains responsible for authenticating and rate-limiting the server endpoint that
mints it.

## ElevenLabs voice orb state mapping

The ElevenLabs adapter is responsible for normalizing provider events into orb-ui states. Keep the UI language simple:

* connecting
* listening
* thinking
* speaking
* error

ElevenLabs input levels are emitted as `inputVolume` while listening, and output levels are emitted as `outputVolume` while speaking.

The ElevenLabs mode surface does not expose a separate thinking event through this adapter, so the
normal transition is listening to speaking. If your application has an authoritative processing
state from another source, use controlled mode and include it in the signal you pass to `Orb`.

## Session ownership

`createElevenLabsAdapter` owns `Conversation.startSession()` and `endSession()`. When an interactive
theme is activated, `Orb` calls the adapter's `start()` or `stop()` method. The adapter installs the
required callbacks, polls both volume directions during the active turn, and clears that polling
when the session ends or the subscriber is removed.

If your product already has permanent call controls, use a passive orb and invoke the adapter from
those controls:

```tsx theme={null}
export function ElevenLabsCallSurface() {
  return (
    <section aria-label="ElevenLabs conversation">
      <Orb adapter={adapter} theme="cloud" interactive={false} />
      <button onClick={() => void adapter.start()}>Start conversation</button>
      <button onClick={() => void adapter.stop()}>End conversation</button>
    </section>
  )
}
```

## Theming

The `circle` theme works well for assistant-style voice interfaces. The `bars` theme is useful when you want a waveform-adjacent feel.

```tsx theme={null}
<Orb adapter={adapter} theme="circle" aria-label="Start ElevenLabs assistant" />
<Orb adapter={adapter} theme="bars" aria-label="Start ElevenLabs assistant" />
```

Clickable themes render as `<button type="button">` controls, so they work with keyboard activation and accept forwarded attributes like `id`, `data-*`, and `aria-label`.

## Controlled mode

If your app owns more of the ElevenLabs session lifecycle, pass a signal directly.

```tsx theme={null}
<Orb signal={{ state, inputVolume, outputVolume }} theme="circle" />
```

Controlled mode is also useful when the same product-level state combines ElevenLabs events with
tool calls, human handoff, or transcript activity. Normalize those sources once and keep the orb
independent of provider-specific event names.

## Troubleshooting

**The orb stays on connecting.** Confirm that `Conversation.startSession()` resolves and that the
browser can reach the agent. For private agents, mint a fresh credential and verify that its
connection type matches the credential shape.

**The orb moves for only one side.** Input volume is read while the normalized state is listening;
output volume is read while it is speaking. Confirm that ElevenLabs mode-change callbacks are
arriving and that the browser granted microphone access.

**A stopped session still updates the UI.** Keep one adapter instance per active configuration and
let React remove its subscription on unmount. If a credential or agent changes, stop the old
adapter before replacing it.

## Production checklist

* Private credentials are minted on an authenticated, rate-limited server endpoint.
* The adapter is created with exactly one of `agentId`, `signedUrl`, or `conversationToken`.
* Microphone denial and session errors have visible recovery copy outside the orb.
* Separate call controls use `interactive={false}` on the visualization.
* Start, stop, and restart are tested without leaked listeners or volume polling.
* A text status or transcript makes the conversation understandable without animation.

## Related

* [React voice agent UI guide](/docs/guides/voice-agent-ui)
* [Adapter overview](/docs/adapters/overview)
* [Voice orb UI example](/docs/examples/voice-orb-ui)
* [Custom integrations](/docs/adapters/custom)
* [Themes and voice states](/docs/themes/voice-states)
