logoassistant-ui

Thread

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

Sample
Hello there!
How can I help you today?

Anatomy

The Thread component is built with the following primitives:

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

<ThreadPrimitive.Root>
  <ThreadPrimitive.Viewport>
    <ThreadPrimitive.Empty />
    <ThreadPrimitive.Messages
      components={{
        EditComposer,
        UserMessage, 
        AssistantMessage,
      }}
    />
    <ThreadPrimitive.ScrollToBottom />
  </ThreadPrimitive.Viewport>
  <ThreadPrimitive.Suggestion />
  <ThreadPrimitive.If />
</ThreadPrimitive.Root>

Getting Started

Add the component

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

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

<ThreadPrimitive.If empty>
  <ThreadWelcome />
</ThreadPrimitive.If>

Viewport Spacer

<ThreadPrimitive.If empty={false}>
  <div className="min-h-8 grow" />
</ThreadPrimitive.If>

Conditional Send/Cancel Button

<ThreadPrimitive.If running={false}>
  <ComposerPrimitive.Send>
    Send
  </ComposerPrimitive.Send>
</ThreadPrimitive.If>

<ThreadPrimitive.If running>
  <ComposerPrimitive.Cancel>
    Cancel
  </ComposerPrimitive.Cancel>
</ThreadPrimitive.If>

Suggestions

<ThreadPrimitive.Suggestion
  prompt="What's the weather in San Francisco?"
  send
/>

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

components:

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

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.

Suggestion

Shows a suggestion to the user. When clicked, replaces the composer's value with the suggestion and optionally sends it.

<ThreadPrimitive.Suggestion
  prompt="Tell me about React hooks"
  send
/>

ThreadPrimitiveSuggestionProps

prompt:

string

The suggestion text to use when clicked.

send?:

boolean

When true, automatically sends the message. When false, replaces or appends the composer text with the suggestion - depending on the value of `clearComposer`

clearComposer:

boolean = true

Whether to clear the composer after sending. When send is set to false, determines if composer text is replaced with suggestion (true, default), or if the suggestion's prompt is appended to the composer text (false).

autoSend?:

boolean

Deprecated. Use 'send' instead.

method?:

'replace'

Deprecated. This parameter is no longer used.

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.

If

Conditionally renders children based on thread state.

ThreadPrimitiveIfProps

empty?:

boolean | undefined

Render children if the thread is empty (no messages).

running?:

boolean | undefined

Render children if the thread is running (assistant is responding).

disabled?:

boolean | undefined

Render children if the thread is disabled.

Multiple conditions can be combined on ThreadPrimitive.If - all specified conditions must match for children to render.

  • ThreadList - List of threads, with or without sidebar