A scroll-aware chat container that auto-sticks to the bottom as new messages stream in.
Start a conversation
This is a simulated conversation
Installation
npx shadcn-svelte@latest add https://sv11.ui.twango.dev/r/conversation.json Usage
<script lang="ts">
import { Conversation } from "$lib/registry/ui/conversation";
</script>
<Conversation /> Examples
Basic Usage
Wrap your message list in Conversation.Root and Conversation.Content. Include Conversation.ScrollButton to give users a way back to the latest turn after they scroll up.
<script lang="ts">
import * as Conversation from "$lib/registry/ui/conversation";
type Turn = { id: string; content: string };
let messages: Turn[] = $state([]);
</script>
<Conversation.Root>
<Conversation.Content>
{#each messages as message (message.id)}
<div>{message.content}</div>
{/each}
</Conversation.Content>
<Conversation.ScrollButton />
</Conversation.Root> With Empty State
Render Conversation.EmptyState when the message list is empty. It accepts title, description, and an icon snippet.
<script lang="ts">
import MessageCircleIcon from "@lucide/svelte/icons/message-circle";
import * as Conversation from "$lib/registry/ui/conversation";
type Turn = { id: string; content: string };
let messages: Turn[] = $state([]);
</script>
<Conversation.Root>
<Conversation.Content>
{#if messages.length === 0}
<Conversation.EmptyState
title="No messages yet"
description="Start a conversation to see messages here"
>
{#snippet icon()}
<MessageCircleIcon class="size-12" />
{/snippet}
</Conversation.EmptyState>
{:else}
{#each messages as message (message.id)}
<div>{message.content}</div>
{/each}
{/if}
</Conversation.Content>
<Conversation.ScrollButton />
</Conversation.Root> API Reference
Notes
- Built on
stick-to-bottom-sveltefor smooth scroll anchoring. - Automatically stays pinned to the bottom as new content is appended, unless the user has scrolled up.
Conversation.ScrollButtononly appears when the user is not at the bottom. It callsscrollToBottom()on the parent context.Conversation.EmptyStateaccepts either strings orSnippets fortitleanddescription, plus a separateiconsnippet. Passingchildrenoverrides the entire default layout.- Sub-components (
Content,ScrollButton,EmptyState) read the parent context and will throw if used outsideConversation.Root. - Pair with
Messagefor styled user/assistant turns, or render any custom markup insideConversation.Content.