<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.

Web conversation demo

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.

Mobile conversation demo

Blocks

The conversation family is intentionally small. It gives you the shared shell around the message list without trying to own the messages themselves.

ComponentRole
ConversationRoot container and scroll-state owner
ConversationContentScrollable content region for messages and states
ConversationScrollButtonJump-to-latest button when the user is away from the bottom
ConversationLoadingLoading surface while the assistant is still working
ConversationErrorError surface with retry affordance
ConversationDownloadExport 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>
  );
}

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.

BehaviorWebMobile
Bottom followingBuilt on StickToBottomManaged with internal scroll state
Jump to latestConversationScrollButton appears when not at bottomSame idea, with native animated visibility
Keyboard handlingNormal desktop scroll behaviorKeyboardFriendlyScrollView 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.

ComponentPurpose
ConversationLoadingShows that the assistant is still thinking or generating
ConversationErrorShows 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.

PlatformBehavior
WebDownloads a .md file
MobileOpens 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.

AreaWebMobile
Root behaviorStickToBottomContext-managed View container
Content regionStickToBottom.ContentKeyboardFriendlyScrollView
Scroll button revealCSS transition-based visibilityReanimated timing-based visibility
Extra spacing helperConversationContentSpacer availableNo 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.

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

On this page

Make AI your edge, not replacement.Get TurboStarter AI