import { ThreadSample } from "@/components/docs/samples/thread";
A complete chat interface that combines message rendering, auto-scrolling, composer input,
attachments, and conditional UI states. Fully customizable and composable.
Anatomy#
The Thread component is built with the following primitives:
import { ThreadPrimitive, AuiIf } from "@assistant-ui/react";
<ThreadPrimitive.Root>
<ThreadPrimitive.Viewport>
<AuiIf condition={(s) => s.thread.isEmpty}>
<ThreadWelcome />
{/* ThreadWelcome includes ThreadPrimitive.Suggestions */}
</AuiIf>
<ThreadPrimitive.Messages>
{({ message }) => {
if (message.role === "user") return <UserMessage />;
return <AssistantMessage />;
}}
</ThreadPrimitive.Messages>
<ThreadPrimitive.ViewportFooter>
<ThreadPrimitive.ScrollToBottom />
<Composer />
</ThreadPrimitive.ViewportFooter>
</ThreadPrimitive.Viewport>
</ThreadPrimitive.Root>
Getting Started#
Add the component#
<InstallCommand shadcn={["thread"]} />
This adds a /components/assistant-ui/thread.tsx file to your project, which you can adjust as needed.
Use in your application#
import { Thread } from "@/components/assistant-ui/thread";
export default function Chat() {
return (
<div className="h-full">
<Thread />
</div>
);
}
Examples#
Welcome Screen#
<AuiIf condition={(s) => s.thread.isEmpty}>
<ThreadWelcome />
</AuiIf>
Viewport Spacer#
<AuiIf condition={(s) => !s.thread.isEmpty}>
<div className="min-h-8 grow" />
</AuiIf>
Conditional Send/Cancel Button#
<AuiIf condition={(s) => !s.thread.isRunning}>
<ComposerPrimitive.Send>
Send
</ComposerPrimitive.Send>
</AuiIf>
<AuiIf condition={(s) => s.thread.isRunning}>
<ComposerPrimitive.Cancel>
Cancel
</ComposerPrimitive.Cancel>
</AuiIf>
Suggestions#
Display suggested prompts using the Suggestions API. See the Suggestions guide for detailed configuration.
import { SuggestionPrimitive, ThreadPrimitive } from "@assistant-ui/react";
// Configure suggestions in your runtime provider
const aui = useAui({
suggestions: Suggestions(["What's the weather?", "Tell me a joke"]),
});
// Display suggestions in your thread component
<ThreadPrimitive.Suggestions>
{() => <SuggestionItem />}
</ThreadPrimitive.Suggestions>
// Custom suggestion item
const SuggestionItem = () => (
<SuggestionPrimitive.Trigger send asChild>
<button>
<SuggestionPrimitive.Title />
</button>
</SuggestionPrimitive.Trigger>
);
API Reference#
The following primitives are used within the Thread component and can be customized in your /components/assistant-ui/thread.tsx file.
Root#
Contains all parts of the thread.
<ParametersTable
type="ThreadPrimitiveRootProps"
parameters={[
{
name: "asChild",
type: "boolean",
default: "false",
description: "Merge props with child element instead of rendering a wrapper div.",
},
{
name: "className",
type: "string",
description: "CSS class name.",
},
]}
/>
This primitive renders a <div> element unless asChild is set.
Viewport#
The scrollable area containing all messages. Automatically scrolls to the bottom as new messages are added.
<ParametersTable
type="ThreadPrimitiveViewportProps"
parameters={[
{
name: "asChild",
type: "boolean",
default: "false",
description: "Merge props with child element instead of rendering a wrapper div.",
},
{
name: "autoScroll",
type: "boolean",
default: "true",
description:
"Whether to automatically scroll to the bottom when new messages are added while the viewport was previously scrolled to the bottom.",
},
{
name: "className",
type: "string",
description: "CSS class name.",
},
]}
/>
This primitive renders a <div> element unless asChild is set.
Messages#
Renders all messages in the thread. This primitive renders a separate component for each message.
<ThreadPrimitive.Messages>
{({ message }) => {
if (message.role === "user") return <UserMessage />;
return <AssistantMessage />;
}}
</ThreadPrimitive.Messages>
<ParametersTable
type="ThreadPrimitiveMessagesProps"
parameters={[
{
name: "components",
type: "MessageComponents",
required: true,
description: "Components to render for different message types.",
children: [
{
type: "MessageComponents",
parameters: [
{
name: "Message",
type: "ComponentType",
description: "Default component for all messages.",
},
{
name: "UserMessage",
type: "ComponentType",
description: "Component for user messages.",
},
{
name: "EditComposer",
type: "ComponentType",
description:
"Component for user messages being edited.",
},
{
name: "AssistantMessage",
type: "ComponentType",
description: "Component for assistant messages.",
},
{
name: "SystemMessage",
type: "ComponentType",
description: "Component for system messages.",
},
],
},
],
},
]}
/>
MessageByIndex#
Renders a single message at the specified index.
<ThreadPrimitive.MessageByIndex
index={0}
components={{
UserMessage: UserMessage,
AssistantMessage: AssistantMessage
}}
/>
<ParametersTable
type="ThreadPrimitiveMessageByIndexProps"
parameters={[
{
name: "index",
type: "number",
required: true,
description: "The index of the message to render.",
},
{
name: "components",
type: "MessageComponents",
description: "Components to render for different message types.",
},
]}
/>
Empty#
Renders children only when there are no messages in the thread.
<ParametersTable
type="ThreadPrimitiveEmptyProps"
parameters={[
{
name: "children",
type: "ReactNode",
description: "Content to display when the thread is empty.",
},
]}
/>
ScrollToBottom#
A button to scroll the viewport to the bottom. Disabled when the viewport is already at the bottom.
<ParametersTable
type="ThreadPrimitiveScrollToBottomProps"
parameters={[
{
name: "asChild",
type: "boolean",
default: "false",
description: "Merge props with child element instead of rendering a wrapper button.",
},
{
name: "className",
type: "string",
description: "CSS class name.",
},
]}
/>
This primitive renders a <button> element unless asChild is set.
Suggestions#
Renders all configured suggestions. Configure suggestions using the Suggestions() API in your runtime provider.
<ThreadPrimitive.Suggestions>
{() => <CustomSuggestionComponent />}
</ThreadPrimitive.Suggestions>
<ParametersTable
type="ThreadPrimitiveSuggestionsProps"
parameters={[
{
name: "components",
type: "{ Suggestion: ComponentType }",
description: "Custom component to render each suggestion.",
},
]}
/>
AuiIf#
Conditionally renders children based on assistant state. This is a generic component that can access thread, message, composer, and other state.
import { AuiIf } from "@assistant-ui/react";
<AuiIf condition={(s) => s.thread.isEmpty}>
<WelcomeScreen />
</AuiIf>
<AuiIf condition={(s) => s.thread.isRunning}>
<LoadingIndicator />
</AuiIf>
<AuiIf condition={(s) => s.message.role === "assistant"}>
<AssistantAvatar />
</AuiIf>
<ParametersTable
type="AuiIfProps"
parameters={[
{
name: "condition",
type: "(state: AssistantState) => boolean",
required: true,
description: "A function that receives the assistant state and returns whether to render children.",
},
]}
/>
Related Components#
- ThreadList - List of threads, with or without sidebar
- Quoting guide - Quote selected text from messages
- SelectionToolbarPrimitive - Floating toolbar API reference