ConversationPreview
Scroll-aware chat conversation container with content, empty state, and scroll-to-bottom UI.
Overview
Conversation wraps the message feed area. It handles scroll positioning through use-stick-to-bottom, but it does not own messages or rendering.
Usage
import { Card, CardMeta } from "@tilt-legal/cubitt-components/card";
import {
Conversation,
ConversationContent,
ConversationDownload,
ConversationEmptyState,
ConversationScrollButton,
ConversationTimeline,
Message,
MessageContent,
MessageResponse,
messagesToMarkdown,
} from "@tilt-legal/cubitt-components/chat-elements";Examples
Default
<Card className="h-80">
<Conversation className="min-h-0">
<ConversationContent>
{messages.map((message) => (
<Message key={message.id} from={message.role}>
<MessageContent>
{message.role === "assistant" ? (
<MessageResponse>{message.text}</MessageResponse>
) : (
message.text
)}
</MessageContent>
</Message>
))}
</ConversationContent>
<ConversationScrollButton />
</Conversation>
</Card>Timeline
const timelineItems = turns.map((turn) => ({
id: turn.id,
targetId: `turn-${turn.id}`,
message: turn.userText,
response: turn.assistantText,
files: turn.files,
}));
<Conversation className="h-96 rounded-xl border border-border-3">
<ConversationTimeline items={timelineItems} />
<ConversationContent className="pl-16">
{turns.map((turn) => (
<div id={`turn-${turn.id}`} key={turn.id} className="scroll-mt-4">
<Message from="user">
<MessageContent>{turn.userText}</MessageContent>
</Message>
<Message from="assistant">
<MessageContent>
<MessageResponse>{turn.assistantText}</MessageResponse>
</MessageContent>
</Message>
</div>
))}
</ConversationContent>
</Conversation>Download
<Card className="h-80">
<CardMeta className="min-h-12 justify-between px-4 py-0">
<p>Lease review conversation</p>
<ConversationDownload
className="static"
filename="conversation.md"
messages={messages}
/>
</CardMeta>
<Conversation className="min-h-0">
<ConversationContent>{messages.map(...)}</ConversationContent>
<ConversationScrollButton />
</Conversation>
</Card>
const markdown = messagesToMarkdown(messages);Empty
<Card className="h-80">
<Conversation className="min-h-0">
<ConversationContent className="h-full">
<ConversationEmptyState
title="No conversation yet"
description="Messages will appear here as the assistant responds."
/>
</ConversationContent>
</Conversation>
</Card>API Reference
Conversation
Scroll-aware conversation root powered by use-stick-to-bottom. Extends StickToBottom props.
| Prop | Type | Default | Description |
|---|---|---|---|
initial | string | "smooth" | Initial scroll behavior passed to StickToBottom. |
resize | string | "smooth" | Resize scroll behavior passed to StickToBottom. |
role | string | "log" | ARIA role for the conversation region. |
className | string | - | Additional CSS classes for the scroll root. |
ConversationContent
Message stack inside Conversation. Extends StickToBottom.Content props.
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | - | Conversation messages or custom content. |
className | string | - | Additional CSS classes for the content. |
ConversationTimeline
Left-side conversation turn index for jumping to user messages. Consumers own turn grouping and target ids; Cubitt owns the rail, hover preview, file indicator, active styling, and scroll affordance.
| Prop | Type | Default | Description |
|---|---|---|---|
items | ConversationTimelineItem[] | - | Prepared turn summaries and target ids. Required. |
label | string | "Conversation timeline" | Accessible label for the timeline navigation. |
maxFiles | number | 2 | Maximum file entities shown before the compact +N summary. |
onNavigate | (item: ConversationTimelineItem) => void | - | Called after a timeline item is selected. |
className | string | - | Additional CSS classes for the positioned timeline navigation. |
ConversationTimelineItem
Prepared display data for one user turn in ConversationTimeline.
| Field | Type | Description |
|---|---|---|
id | string | Stable item id. |
targetId | string | DOM id of the message or turn wrapper to scroll to. |
message | string | User message preview. Rendered as one small-size line. |
response | string | Optional agent response preview. Rendered as small clamped text. |
files | ChatAttachmentFileData[] | Optional files shown as compact entities and rail dots. |
label | string | Optional accessible label for the timeline anchor. |
ConversationEmptyState
Centered empty state for conversations. Extends all HTML div props.
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | "No messages yet" | Heading shown when children is not provided. |
description | string | "Start a conversation to see messages here" | Supporting text shown when children is not provided. |
icon | React.ReactNode | - | Optional icon shown above the default title/description. |
children | React.ReactNode | - | Custom empty-state content. Replaces title/description UI. |
className | string | - | Additional CSS classes for the empty-state wrapper. |
ConversationScrollButton
Scroll-to-bottom affordance for Conversation. Extends Button props and only renders when the conversation is not at the bottom.
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | "Scroll to bottom" | Accessible label for the icon button. |
onClick | React.MouseEventHandler<HTMLButtonElement> | - | Called after the internal scrollToBottom() handler. |
className | string | - | Additional CSS classes for the positioned button. |
ConversationDownload
Download button that converts messages to markdown and saves a .md file. Extends Button props except onClick.
| Prop | Type | Default | Description |
|---|---|---|---|
messages | UIMessage[] | - | Messages to serialize. Required. |
filename | string | "conversation.md" | Download filename. |
formatMessage | (message: UIMessage, index: number) => string | default formatter | Optional formatter for each message before joining. |
aria-label | string | "Download conversation" | Accessible label for the icon button. |
children | React.ReactNode | download icon | Custom button content. |
className | string | - | Additional CSS classes for the positioned button. |
messagesToMarkdown
Utility used by ConversationDownload.
| Parameter | Type | Default | Description |
|---|---|---|---|
messages | UIMessage[] | - | Messages to serialize. Required. |
formatMessage | (message: UIMessage, index: number) => string | default formatter | Optional formatter for each serialized message. |