- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Code Block
- Collapsible
- Combobox
- Command
- Context Menu
- Data Table
- Date Picker
- Dialog
- Direction
- Drawer
- Dropdown Menu
- Empty
- Field
- Hover Card
- Input
- Input Group
- Input OTP
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Native Select
- Navigation Menu
- Pagination
- Popover
- Progress
- Questionnaire
- Radio Group
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Toggle Group
- Tooltip
- Trace
- Typography
"use client"
import * as React from "react"Chat is the thread of a conversation with the agent. Each turn is a Message inside Message Scroller. The person's message is the scroll anchor, so a new question settles near the top and the reply streams in under it. The field is a Prompt Input.
Installation#
Chat is built with the Message Scroller, Message, Bubble, and Input Group components.
pnpm dlx shadcn@latest add message-scroller message bubble input-group
Usage#
<MessageScrollerProvider autoScroll>
<MessageScroller>
<MessageScrollerViewport>
<MessageScrollerContent>
<MessageScrollerItem messageId={id} scrollAnchor>
<Message align="end">
<MessageContent>
<Bubble align="end" variant="muted">
<BubbleContent>{text}</BubbleContent>
</Bubble>
</MessageContent>
</Message>
</MessageScrollerItem>
</MessageScrollerContent>
</MessageScrollerViewport>
<MessageScrollerButton />
</MessageScroller>
</MessageScrollerProvider>scrollAnchor goes on the person's turn. autoScroll follows the reply while the person stays at the live edge.
See Message Scroller.
AI SDK Helper#
To build without a model, script the conversation with createChat. Each agent turn streams reasoning, a tool call and a reply. chat.next() fills the field with the next scripted message, and a string fallback replies once the script runs out.
const chat = createChat()
.user("Add a dark mode toggle to the settings page.")
.assistant(({ writer }) => {
writer.reasoning("I need to know how themes are set up first.")
writer
.tool("runCommand", { input: { command: "rg -l ThemeProvider app" } })
.output({ stdout: "app/layout.tsx" })
writer.text("next-themes is already set up, so I added a ModeToggle.")
})
const transport = chat.transport({
fallback:
"This demo only knows the scripted turns. Press reset to start over.",
})
const nextMessage = chat.next(messages)Migrating from AI SDK Elements#
If you added Conversation from AI SDK Elements, your project has a components/ai-elements/conversation.tsx wrapper. Paste this prompt into your coding agent to rebuild it on Message Scroller. Every export keeps its name and props, so the code that uses it keeps working.
Migrate components/ai-elements/conversation.tsx from AI SDK Elements to the shadcn/ui Message Scroller component.
Keep every export, with the same name and props, so the code that imports it keeps working. Only change how each part renders.
1. Install Message Scroller: npx shadcn@latest add message-scroller
2. Read https://ui.shadcn.com/docs/ai-elements/base/chat.md and build Chat the same way.
3. Rebuild each part:
- Conversation renders a MessageScrollerProvider with autoScroll and a MessageScroller, instead of StickToBottom.
- ConversationContent renders a MessageScrollerViewport and a MessageScrollerContent.
- ConversationScrollButton renders a MessageScrollerButton.
- Keep ConversationEmptyState, ConversationDownload and messagesToMarkdown as they are.
4. In the code that renders the thread, wrap each message in a MessageScrollerItem with its messageId, and set scrollAnchor on the person's messages. This is the only change outside the file.
5. Remove use-stick-to-bottom from the dependencies.
6. Remove the imports the file no longer uses.
7. Run the type checker and fix any errors.