# Thread URL: /docs/primitives/thread Build custom scrollable message containers with auto-scroll, empty states, and message rendering. The Thread primitive is the scrollable message container, and the backbone of any chat interface. It handles viewport management, auto-scrolling, empty states, message rendering, and suggestions. You provide the layout and styling. ```tsx import { AuiIf, ComposerPrimitive, ThreadPrimitive, MessagePrimitive, } from "@assistant-ui/react"; import { ArrowUpIcon } from "lucide-react"; function MinimalThread() { return ( s.thread.isEmpty}>

Welcome! Ask a question to get started.

{({ message }) => { if (message.role === "user") return ; return ; }}
); } function UserMessage() { return (
); } function AssistantMessage() { return (
); } ```
Quick Start \[#quick-start] Minimal example: ```tsx import { ThreadPrimitive } from "@assistant-ui/react"; {() => } ``` `Root` renders a `
`, `Viewport` renders a scrollable `
`, and `Messages` iterates over the thread's messages. Add your own styles and components; the primitive handles the rest. Runtime setup: primitives require runtime context. Wrap your UI in `AssistantRuntimeProvider` with a runtime (for example `useLocalRuntime(...)`). See [Pick a Runtime](/docs/runtimes/pick-a-runtime). Core Concepts \[#core-concepts] Viewport & Auto-Scroll \[#viewport--auto-scroll] `Viewport` is the scrollable container. It auto-scrolls to the bottom as new content streams in, but only if the user hasn't scrolled up manually. Set `autoScroll={false}` to disable this entirely. ```tsx {/* messages */} ``` Turn Anchor \[#turn-anchor] By default, new messages appear at the bottom and scroll down. With `turnAnchor="top"`, the user's message anchors to the top of the viewport. This creates the modern reading experience where you see the question at the top and the response flowing below it. ```tsx {/* messages */} ``` This is what the shadcn Thread component uses by default. For scroll anchoring to work correctly, `ViewportSlack` is needed on the last assistant message to provide enough min-height for the user message to anchor at the top. This is included automatically in the shadcn component. Viewport Scroll Options \[#viewport-scroll-options] `ThreadPrimitive.Viewport` has three event-specific scroll controls: * `scrollToBottomOnRunStart` (default `true`): scrolls when `thread.runStart` fires * `scrollToBottomOnInitialize` (default `true`): scrolls when `thread.initialize` fires * `scrollToBottomOnThreadSwitch` (default `true`): scrolls when `threadListItem.switchedTo` fires These work alongside `autoScroll`. If `autoScroll` is omitted, it defaults to `true` for `turnAnchor="bottom"` and `false` for `turnAnchor="top"`. ```tsx {({ message }) => { if (message.role === "user") return ; return ; }} ``` ViewportFooter \[#viewportfooter] `ViewportFooter` sticks to the bottom of the viewport and registers its height so the auto-scroll system accounts for it. This is where you place your composer: ```tsx {() => } ``` Empty State \[#empty-state] `ThreadPrimitive.Empty` is deprecated. Use [`AuiIf`](/docs/api-reference/primitives/assistant-if) instead. ```tsx s.thread.isEmpty}>

Welcome!

How can I help you today?

``` Messages Iterator \[#messages-iterator] `Messages` now prefers a children render function. It gives you the current message state so you can branch inline: ```tsx {({ message }) => { if (message.composer.isEditing) return ; if (message.role === "user") return ; return ; }} ``` `components` is deprecated. Use the `children` render function instead. Suggestions Iterator \[#suggestions-iterator] `Suggestions` follows the same pattern. Prefer the children render function when rendering custom suggestion UIs: ```tsx {() => } ``` Parts \[#parts] Root \[#root] Top-level container for a thread layout. Renders a `
` element unless `asChild` is set. ```tsx {() => } ``` Viewport \[#viewport] The scrollable area with auto-scroll behavior. Renders a `
` element unless `asChild` is set. ```tsx {({ message }) => { if (message.role === "user") return ; return ; }} ``` ViewportFooter \[#viewportfooter-1] Footer container that registers its height with the viewport scroll system. Renders a `
` element unless `asChild` is set. ```tsx ... ``` ViewportProvider \[#viewportprovider] Provides viewport context without rendering a scrollable element. Use this when you have a custom scroll container. ```tsx
{() => }
``` ViewportSlack \[#viewportslack] Adds min-height for scroll anchoring with `turnAnchor="top"`. It wraps its child element via `Slot` and does not render a DOM element of its own. ```tsx
``` Props: `fillClampThreshold` and `fillClampOffset` control how the slack height is calculated. `children` is required. Messages \[#messages] Renders a component for each message in the thread, resolved by role and edit state. ```tsx {({ message }) => { if (message.composer.isEditing) return ; if (message.role === "user") return ; return ; }} ``` MessageByIndex \[#messagebyindex] Renders a single message at a specific index in the thread. ```tsx ``` ScrollToBottom \[#scrolltobottom] Scrolls the viewport to the bottom. Automatically disabled when already at the bottom. Renders a `