assistant-ui logo/Docs/Components

Thread Component

Stream-ready React chat container with message list, composer, auto-scroll, and accessibility built in. Drop into any AI chat UI built with assistant-ui.

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

How can I help you today?

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

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>
  {() => <SuggestionItem />}
</ThreadPrimitive.Suggestions>

// Custom suggestion item
const SuggestionItem = () => (
  <SuggestionPrimitive.Trigger send asChild>
    <button>
      <SuggestionPrimitive.Title />
    </button>
  </SuggestionPrimitive.Trigger>
);

Component Overrides

Thread accepts an optional components prop that swaps parts of the rendering without editing the copied file. All slots are optional; omitted slots keep the built-in rendering.

import { Thread, type ThreadComponents } from "@/components/assistant-ui/thread";

const THREAD_COMPONENTS: ThreadComponents = {
  ToolFallback: MyToolFallback,
  ToolGroup: MyToolGroup,
};

export default function Chat() {
  return <Thread components={THREAD_COMPONENTS} />;
}
ThreadComponents
AssistantMessage ?: ComponentType

Replaces the entire assistant message, including the action bar and branch picker.

Welcome ?: ComponentType

Replaces the welcome screen shown for a new chat.

ToolFallback ?: ToolCallMessagePartComponent

Renders tool calls that have no tool UI registered by name. Registered tool UIs take precedence over this slot.

ToolGroup ?: ComponentType<PropsWithChildren<{ group: ThreadGroupPart }>>

Wraps runs of consecutive tool calls. Receives the group part (`indices`, `status`) and the rendered children.

ReasoningGroup ?: ComponentType<PropsWithChildren<{ group: ThreadGroupPart }>>

Wraps runs of consecutive reasoning parts. Receives the group part and the rendered children.

Define the components object once at module scope (or memoize it) so message subtrees do not re-render whenever the parent re-renders.

For per-tool UI, prefer registering a renderer by tool name over overriding ToolFallback: put render on the matching toolkit entry (see Tool UI). data message parts render through renderers registered with useAssistantDataUI; parts without a registered renderer are not displayed.

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 (false when turnAnchor is "top")

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

turnAnchor : "top" | "bottom" = "bottom"

Controls scroll anchoring behavior for new messages. "top" anchors new user messages at the top of the viewport.

topAnchorMessageClamp : { tallerThan?: string; visibleHeight?: string } = { tallerThan: "10em", visibleHeight: "6em" }

Clamps tall user messages when turnAnchor is "top". Messages up to `tallerThan` stay fully visible; taller messages show only `visibleHeight` of their bottom edge above the assistant response.

tallerThan : string = "10em"

Clamp messages taller than this CSS length.

visibleHeight : string = "6em"

Visible portion of a clamped message's bottom edge.

scrollToBottomOnRunStart : boolean = true

Whether to scroll to bottom when a new run starts.

scrollToBottomOnInitialize : boolean = true

Whether to scroll to bottom when thread history is first loaded.

scrollToBottomOnThreadSwitch : boolean = true

Whether to scroll to bottom when switching to a different thread.

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>
  {({ message }) => {
    if (message.role === "user") return <UserMessage />;
    return <AssistantMessage />;
  }}
</ThreadPrimitive.Messages>
ThreadPrimitiveMessagesProps
components : MessageComponents

Components to render for different message types.

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
index : 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>
  {() => <CustomSuggestionComponent />}
</ThreadPrimitive.Suggestions>
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
condition : (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.