A compact conversation input bar with voice and text modes. Provider-agnostic — pass a ConversationAdapter wired to ElevenLabs, OpenAI Realtime, or any voice/chat backend.
ConversationBar is provider-agnostic — pass
a ConversationAdapter wired to ElevenLabs, OpenAI
Realtime, or any other voice/chat backend. This demo has no backend hooked up.
Installation
npx shadcn-svelte@latest add https://sv11.ui.twango.dev/r/conversation-bar.json Usage
<script lang="ts">
import { ConversationBar } from "$lib/registry/ui/conversation-bar";
</script>
<ConversationBar /> <ConversationBar> requires an adapter prop — see Providers for the interface and Adapters for provider recipes.
Examples
Basic Usage
Pass any object that matches ConversationAdapter and wire up the lifecycle callbacks you care about.
<script lang="ts">
import { ConversationBar } from "$lib/registry/ui/conversation-bar";
import type { ConversationAdapter } from "$lib/registry/ui/conversation-bar";
const adapter: ConversationAdapter = createMyAdapter(/* ... */);
</script>
<ConversationBar
{adapter}
onConnect={() => console.log("Connected")}
onDisconnect={() => console.log("Disconnected")}
onMessage={(message) => console.log("Message:", message)}
onError={(error) => console.error("Error:", error)}
/> Custom Styling
Merge extra classes onto the container with class, and onto the inner waveform container with waveformClassName.
<ConversationBar
{adapter}
class="max-w-2xl"
waveformClassName="bg-gradient-to-r from-blue-500 to-purple-500"
/> Custom Disconnected Label
Replace the placeholder text shown in the waveform slot while the session is idle.
<ConversationBar {adapter} disconnectedText="Tap to call support" /> Collecting Messages
Accumulate the incoming ConversationMessage stream into reactive state — handy for pairing ConversationBar with a transcript or chat view.
<script lang="ts">
import { ConversationBar } from "$lib/registry/ui/conversation-bar";
import type { ConversationAdapter, ConversationMessage } from "$lib/registry/ui/conversation-bar";
const adapter: ConversationAdapter = createMyAdapter(/* ... */);
let messages: ConversationMessage[] = $state([]);
</script>
<ConversationBar {adapter} onMessage={(m) => (messages = [...messages, m])} /> API Reference
Notes
- Every method on
ConversationAdapteris driven from user interaction: the mic, keyboard, phone, and send buttons callsetMuted,sendContextualUpdate,disconnect/connect, andsendMessagerespectively. - The component tracks an internal
sessionIdso late callbacks from a priorconnect()are ignored — swapping adapters or rapid connect/disconnect cycles cannot leak stale state into the UI. - Text input fires
sendContextualUpdate(text)on every keystroke while the keyboard is open, andsendMessage(text)onEnter(shift-enter inserts a newline). Adapters that do not implement contextual updates can makesendContextualUpdatea no-op. - The waveform visualizes the active microphone stream while
connected && !muted. The mic, keyboard, and end-call buttons are disabled outside theconnectedstate. onDestroycallsadapter.disconnect()if a session is still active, so unmounting the component always releases the underlying resources.adapteris a required prop onConversationBarProps, so TypeScript will flag its omission at compile time. At runtime, pressing the phone button without an adapter would throw fromconnect()— the demo on this page uses a stub adapter that always rejects there to illustrate theonErrorpath.