Docs Quickstart
Quickstart
A complete voice panel in one component, then the signed URL switch you need for a private agent.
Your first session
Call useConversation during component initialisation. It creates a controller and registers
a teardown effect, so there is no socket or audio graph to close by hand.
<script lang="ts"> import { useConversation } from 'svelte-elevenlabs'; const conversation = useConversation({ agentId: 'agent_your_public_id' }); function toggle(): void { if (conversation.status === 'disconnected') { void conversation.startConversation(); return; } conversation.stopConversation(); }</script><button onclick={toggle} disabled={conversation.status === 'connecting'}> {conversation.status === 'connected' ? 'End session' : 'Start session'}</button><p>Status: {conversation.status}</p>{#each conversation.transcripts as message (message.id)} <p><strong>{message.role}:</strong> {message.text}</p>{/each}For a voice session, call startConversation() from a user gesture. Microphone
permission and both AudioContext instances are created inside that click, which keeps
playback from being suspended behind an autoplay gate.
The promise resolves after the handshake metadata or after a start failure. Connection and audio
failures appear through error, errorCode, and onError instead of a rejected promise, so void conversation.startConversation() is safe.
Live state
Every value below is Svelte state. Read it directly in markup or derive from it with $derived.
| Value | Type | Meaning |
|---|---|---|
status | disconnected | connecting | connected | Connection lifecycle. |
isSpeaking | boolean | Agent audio is scheduled or playing. |
isListening | boolean | Microphone is live and the agent can hear it. |
isUserSpeaking | boolean | Local voice activity detection hears the user. |
isMuted | boolean | Outgoing microphone audio is stopped. |
volume | number | Loudest normalized level from 0 to 1. |
transcripts | TranscriptMessage[] | Stable entries, including partial agent text. |
error | string | null | Human-readable message that is safe to render. |
errorCode | ConversationErrorCode | null | Stable category for branching in your UI. |
latency | number | null | Latest time to first agent audio, in milliseconds. |
queueStatus | waiting | admitted | timed_out | null | Concurrency queue state when the agent is at capacity. |
A small error banner can use the message directly:
{#if conversation.error} <p role="alert">{conversation.error}</p> <button onclick={() => conversation.clearError()}>Dismiss</button>{/if}Signed mode
Public agent ids connect directly. For a private agent, or for production traffic where the API key must stay on the server, ask the controller to exchange the id for a short-lived URL:
const conversation = useConversation({ agentId: 'agent_your_public_id', signedUrlEndpoint: '/api/signed-url'});await conversation.startConversation(undefined, { signed: true });The default endpoint is already /api/signed-url, so the option is only needed when
your route lives somewhere else. A target that is already a full WebSocket URL bypasses the
exchange and connects directly.
Ready-made widget
If you want the full control surface without writing it, use ConversationWidget.
Pass an agent id and it owns the session, or pass a controller you already created.
<script lang="ts"> import { ConversationWidget } from 'svelte-elevenlabs';</script><ConversationWidget agentId="agent_your_public_id" signed title="Talk to the agent" subtitle="Live voice and text"/>The widget includes the target field, visualizer, transcript, text composer, feedback buttons,
playback volume, device selectors, and telemetry. The target field, visualizer, transcript,
telemetry, composer, feedback, and audio controls each have a show* prop. Read Components for the full prop list and theme variables.