Thread

The main chat container with messages, composer, and auto-scroll.

A complete chat interface that combines message rendering, auto-scrolling, composer input, attachments, and conditional UI states. Fully customizable and composable.

Hello there!

How can I help you today?

Anatomy

The Thread component is built with the following primitives:

import { ThreadPrimitive, SelectionToolbarPrimitive, AuiIf } from "@assistant-ui/react";

<ThreadPrimitive.Root>
  <ThreadPrimitive.Viewport>
    <AuiIf condition={(s) => s.thread.isEmpty} />
    <ThreadPrimitive.Messages
      components={{
        EditComposer,
        UserMessage,
        AssistantMessage,
      }}
    />
    <ThreadPrimitive.ScrollToBottom />
  </ThreadPrimitive.Viewport>
  <ThreadPrimitive.Suggestions />
  <AuiIf condition={...} />

  {/* Floating toolbar — appears when text is selected in a message */}
  <SelectionToolbarPrimitive.Root>
    <SelectionToolbarPrimitive.Quote>Quote</SelectionToolbarPrimitive.Quote>
  </SelectionToolbarPrimitive.Root>
</ThreadPrimitive.Root>

Getting Started

Add the component

npx shadcn@latest add https://r.assistant-ui.com/thread.json

This adds a /components/assistant-ui/thread.tsx file to your project, which you can adjust as needed.

Use in your application

/app/page.tsx
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
  components={{
    Suggestion: SuggestionItem,
  }}
/>

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

ThreadPrimitiveRootProps
asChild: boolean= false

Merge props with child element instead of rendering a wrapper div.

className?: string

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.

ThreadPrimitiveViewportProps
asChild: boolean= false

Merge props with child element instead of rendering a wrapper div.

autoScroll: boolean= true

Whether to automatically scroll to the bottom when new messages are added while the viewport was previously scrolled to the bottom.

className?: string

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
  components={{
    UserMessage: UserMessage,
    EditComposer: EditComposer,
    AssistantMessage: AssistantMessage,
  }}
/>
ThreadPrimitiveMessagesProps
componentsrequired: MessageComponents

Components to render for different message types.

MessageComponents
Message?: ComponentType

Default component for all messages.

UserMessage?: ComponentType

Component for user messages.

EditComposer?: ComponentType

Component for user messages being edited.

AssistantMessage?: ComponentType

Component for assistant messages.

SystemMessage?: ComponentType

Component for system messages.

MessageByIndex

Renders a single message at the specified index.

<ThreadPrimitive.MessageByIndex
  index={0}
  components={{
    UserMessage: UserMessage,
    AssistantMessage: AssistantMessage
  }}
/>
ThreadPrimitiveMessageByIndexProps
indexrequired: number

The index of the message to render.

components?: MessageComponents

Components to render for different message types.

Empty

Renders children only when there are no messages in the thread.

ThreadPrimitiveEmptyProps
children?: ReactNode

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.

ThreadPrimitiveScrollToBottomProps
asChild: boolean= false

Merge props with child element instead of rendering a wrapper button.

className?: string

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
  components={{
    Suggestion: CustomSuggestionComponent,
  }}
/>
ThreadPrimitiveSuggestionsProps
components?: { Suggestion: ComponentType }

Custom component to render each suggestion.

See the Suggestions guide for detailed information on configuring and customizing suggestions.

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>
AuiIfProps
conditionrequired: (state: AssistantState) => boolean

A function that receives the assistant state and returns whether to render children.

The condition function receives an AssistantState object with access to thread, message, composer, part, and attachment state depending on context.