Conversation
Installation
npx @mielui/svelte add conversationyarn dlx @mielui/svelte add conversationbunx @mielui/svelte add conversationUsage
Give Root a bounded height so Content can scroll. Bind follow when the surrounding interface needs to reflect whether new output is being followed.
The scroll button runs your click handler first. Call preventDefault() to keep the current scroll position and follow state.
<script lang="ts">
import * as Conversation from '@mielui/svelte/components/conversation';
import * as Message from '@mielui/svelte/components/message';
let follow = $state(true);
</script>
<Conversation.Root bind:follow class="h-[32rem]">
<Conversation.Content aria-label="Support conversation">
<Message.Root from="assistant">
<Message.Content>I found the failed request.</Message.Content>
</Message.Root>
</Conversation.Content>
<Conversation.ScrollButton />
</Conversation.Root>Scroll ownership
Conversation follows new output while the reader is at the bottom. Scrolling up sets follow to false and keeps their position as messages arrive. threshold is the distance from the bottom, 80 pixels by default, that still counts as following. ScrollButton returns to the latest message and resumes following.
Examples
Incident review
A longer transcript with reasoning, markdown, and a code block inside the messages.
Separated provider, database, and inventory spans, then matched the first regression to the release boundary.
What changed
The latency increase starts in POST /checkout immediately after release web-2418.
- Address validation added 430 ms at p95.
- The provider timed out for 8% of non-US requests.
- Database and inventory spans stayed within baseline.
Roll back the synchronous validation call, then keep the rule behind the existing review queue.
Use the targeted flag first:
await flags.disable('checkout.address-verification');
await metrics.waitForRecovery({ window: "10m" });This avoids reverting the unrelated tax fixes in the same release.
Empty state
Empty shows a chat icon, a title and a description by default. Pass icon, title, description and action to replace them. To only translate the default title and description, use labels on Root.
Plan the next release
Turn an issue or change set into a staged rollout with clear checks.
Follow live output
Following latest output
Scroll up to pause following. Append more output, then use the arrow to return to the latest step.
Layout and parts
Content is the scroll viewport, and the messages sit in a stack inside it. class styles the viewport, while transcriptClass styles that inner stack, which is where the gap between messages, the side padding and a maximum width belong.
ScrollButton reads its accessible name from labels.scrollToBottom on Root, which defaults to Scroll to latest message. Root, Content and Empty render their children and accept class and style like any element.
Labels
Built-in text is English by default. Pass a labels object of type ConversationLabels to Conversation.Root to translate the transcript's accessible name, the scroll button's name, and the default title and description of Empty. Every key is optional; omitted keys keep their default. An aria-label on Content and a title or description on Empty win over labels. Empty also reads labels when it renders outside Root, and falls back to English there.
<script lang="ts">
import * as Conversation from '@mielui/svelte/components/conversation';
import type { ConversationLabels } from '@mielui/svelte/components/conversation';
const labels: ConversationLabels = {
transcript: 'Conversation',
scrollToBottom: 'Aller au dernier message',
emptyTitle: 'Dรฉmarrer une conversation',
emptyDescription: 'Posez une question ou dรฉcrivez votre travail.'
};
</script>
<Conversation.Root {labels} class="h-[32rem]">
<Conversation.Content>
<Conversation.Empty />
</Conversation.Content>
<Conversation.ScrollButton />
</Conversation.Root>API reference
Conversation.Root
Manages transcript scrolling.
labelsConversationLabelsOverrides the built-in text and accessible names. Every key is optional and English is the fallback.
followBindablebooleanWhether new content should keep the transcript pinned to its latest message.
Default
truethresholdnumberDistance from the bottom, in pixels, that still counts as following.
Default
80childrenSnippet<[]>Content rendered inside.
class(string & (ClassValue | null))Classes added to the element.
Conversation.Content
Contains conversation messages.
transcriptClassstringClasses applied to the inner transcript stack rather than the scroll viewport.
class(string & (ClassValue | null))Classes added to the element.
childrenSnippet<[]>Content rendered inside.
Conversation.Empty
Renders an empty conversation state.
iconSnippet<[]>Icon shown above the title.
titlestringHeading of the empty state.
descriptionstringText under the heading.
actionSnippet<[]>Action shown under the text.
childrenSnippet<[]>Content rendered inside.
class(string & (ClassValue | null))Classes added to the element.
Conversation.ScrollButton
Scrolls to the latest message.
classstringClasses added to the element.
pnpm dlx @mielui/svelte add conversation