Skip to documentation
svelte/elevenlabs Docs

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.

VoicePanel.svelte
<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.

Values you will use first
ValueTypeMeaning
statusdisconnected | connecting | connectedConnection lifecycle.
isSpeakingbooleanAgent audio is scheduled or playing.
isListeningbooleanMicrophone is live and the agent can hear it.
isUserSpeakingbooleanLocal voice activity detection hears the user.
isMutedbooleanOutgoing microphone audio is stopped.
volumenumberLoudest normalized level from 0 to 1.
transcriptsTranscriptMessage[]Stable entries, including partial agent text.
errorstring | nullHuman-readable message that is safe to render.
errorCodeConversationErrorCode | nullStable category for branching in your UI.
latencynumber | nullLatest time to first agent audio, in milliseconds.
queueStatuswaiting | admitted | timed_out | nullConcurrency queue state when the agent is at capacity.

A small error banner can use the message directly:

SVELTE
{#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:

TS
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.

App.svelte
<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.

Edit this page on GitHub