<Conversation />
A conversation container for web and mobile AI interfaces, with scroll-to-bottom behavior, loading and error states, content layout, and conversation export helpers.
<Conversation /> is the outer shell for the chat and transcript experience in the AI starter. It is responsible for the part around the messages: scrolling, bottom-follow behavior, loading and error surfaces, and small utilities like export and “jump to latest”.
Web
The web implementation is built around a stick-to-bottom pattern, which makes it well suited for streaming AI interfaces where new content keeps arriving. It handles the “follow the latest message unless the user scrolls away” behavior for you.

Mobile
The mobile implementation uses a keyboard-friendly scroll container and a small internal context to manage scroll state. It is designed for transcript and chat surfaces that need to stay usable while the keyboard opens and closes.

Blocks
The conversation family is intentionally small. It gives you the shared shell around the message list without trying to own the messages themselves.
| Component | Role |
|---|---|
Conversation | Root container and scroll-state owner |
ConversationContent | Scrollable content region for messages and states |
ConversationScrollButton | Jump-to-latest button when the user is away from the bottom |
ConversationLoading | Loading surface while the assistant is still working |
ConversationError | Error surface with retry affordance |
ConversationDownload | Export helper for the current conversation |
On web, the family also includes ConversationContentSpacer, which is useful when the layout needs extra breathing room below the latest message.
Usage
The common pattern is a root conversation container, a ConversationContent region that holds your message list, and then optional utilities like a loading row, error state, or scroll button.
The web version is driven by the underlying StickToBottom component, so its props are best understood as scroll-behavior props plus layout props. The most common pieces are the root container, content area, loader, error, and floating scroll button.
import {
Conversation,
ConversationContent,
ConversationError,
ConversationLoading,
ConversationScrollButton,
} from "@workspace/ui-web/ai-elements/conversation";
export function ChatSurface() {
return (
<Conversation className="min-h-[32rem]">
<ConversationContent className="pb-20">
<div className="space-y-4">
<div>First message</div>
<div>Second message</div>
</div>
<ConversationLoading />
<ConversationError
error={new Error("Something went wrong")}
onRetry={() => undefined}
/>
</ConversationContent>
<ConversationScrollButton />
</Conversation>
);
}The mobile version follows the same structure, but the content area is backed by KeyboardFriendlyScrollView. That makes it a better fit for full-screen conversation surfaces that need to stay stable while the user types.
import {
Conversation,
ConversationContent,
ConversationError,
ConversationLoading,
ConversationScrollButton,
} from "@workspace/ui-mobile/ai-elements/conversation";
export function ChatSurface() {
return (
<Conversation>
<ConversationContent contentContainerClassName="pb-20">
<ConversationLoading />
<ConversationError
error={new Error("Something went wrong")}
onRetry={() => undefined}
/>
</ConversationContent>
<ConversationScrollButton />
</Conversation>
);
}Scroll behavior
The most important thing this family does is manage how the conversation behaves as new content arrives. That is the difference between a usable chat surface and one that constantly fights the user.
| Behavior | Web | Mobile |
|---|---|---|
| Bottom following | Built on StickToBottom | Managed with internal scroll state |
| Jump to latest | ConversationScrollButton appears when not at bottom | Same idea, with native animated visibility |
| Keyboard handling | Normal desktop scroll behavior | KeyboardFriendlyScrollView keeps input flows usable |
This is why Conversation matters even though it looks visually simple. It is carrying a lot of interaction behavior that would otherwise get rewritten in every chat screen.
Loading and error states
Loading and error surfaces are part of the conversation family because they belong to the conversation flow, not to any single message. They are best treated as rows inside ConversationContent, not as separate overlays.
| Component | Purpose |
|---|---|
ConversationLoading | Shows that the assistant is still thinking or generating |
ConversationError | Shows an error message and exposes a retry action |
On web, ConversationLoading also supports an image-style loading variant, which is useful in multimodal or vision flows where a plain spinner feels too generic.
Exporting a conversation
Both platforms include ConversationDownload, but the behavior is platform-specific. The helper takes an array of conversation messages and turns them into markdown before exporting or sharing.
| Platform | Behavior |
|---|---|
| Web | Downloads a .md file |
| Mobile | Opens the native share sheet |
That makes it a nice utility to keep near the conversation shell rather than rebuilding export logic around the app every time.
Platform differences
The structure is shared, but the implementation still respects the platform.
| Area | Web | Mobile |
|---|---|---|
| Root behavior | StickToBottom | Context-managed View container |
| Content region | StickToBottom.Content | KeyboardFriendlyScrollView |
| Scroll button reveal | CSS transition-based visibility | Reanimated timing-based visibility |
| Extra spacing helper | ConversationContentSpacer available | No spacer helper in the same form |
That split keeps the API familiar while still letting each platform solve the scrolling problem in the way that makes the most sense.
In the starter
The conversation shell is where Message, Attachments, Reasoning, Tool, and loading states all come together. It is the structural surface that turns those individual pieces into a working conversation.
If PromptInput starts the interaction and Message renders individual entries, Conversation is what gives the whole exchange its behavior as a live interface.
Related components
The conversation shell is closely tied to the rest of the AI chat surface. These are the most useful companion pages in the docs set.
How is this guide?
Last updated on
<Context />
A compact context-usage surface for showing token consumption, model limits, and estimated cost on both web and mobile AI interfaces.
<Message />
A composable AI message UI for web and mobile, with content, actions, markdown response rendering, and branch navigation for alternate generations.