--- title: Thread description: Scrollable chat thread with stick-to-bottom behavior and an optional jump-to-bottom control --- import ThreadDefault from "@/components/nexus-ui/examples/thread/default"; A viewport for stacked **[Message](/docs/components/message)** turns (or any content) that **sticks to the bottom** as content grows—built on [**use-stick-to-bottom**](https://github.com/stackblitz/use-stick-to-bottom). **`Thread`** wraps the scroll root, **`ThreadContent`** wraps the scrolling column, and **`ThreadScrollToBottom`** shows a control when the user has scrolled away from the bottom. ## Installation ```bash npx shadcn@latest add @nexus-ui/thread ``` ```bash pnpm dlx shadcn@latest add @nexus-ui/thread ``` ```bash yarn dlx shadcn@latest add @nexus-ui/thread ``` ```bash bunx shadcn@latest add @nexus-ui/thread ```

Copy and paste the following code into your project.

Update the import paths to match your project setup.

## Usage ```tsx keepBackground import { Thread, ThreadContent, ThreadScrollToBottom, } from "@/components/nexus-ui/thread"; ``` ```tsx keepBackground {/* messages */} ``` **`ThreadScrollToBottom`** must be rendered **inside** **`Thread`** so it can read **`useStickToBottomContext`**. ## Vercel AI SDK Integration Render [`useChat`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) messages inside **Thread** by mapping the same **`messages`** array you would render with **[Message](/docs/components/message)** alone. Read each **[`UIMessage`](https://ai-sdk.dev/docs/reference/ai-sdk-core/ui-message)** **`parts`** array and join **`text`** parts for **MessageMarkdown** (streaming updates apply as the SDK appends or grows **`TextUIPart`** content). See [Prompt Input](/docs/components/prompt-input#vercel-ai-sdk-integration) for a minimal **`POST /api/chat`** route with **`streamText`** and **`toUIMessageStreamResponse`**.

Install the AI SDK

```bash npm install ai @ai-sdk/react @ai-sdk/openai ```

Create your chat API route

Use the same handler as in the Prompt Input docs: **`messages: await convertToModelMessages(messages)`** and **`return result.toUIMessageStreamResponse()`**.

Map `messages` to Message inside Thread

Use **`isTextUIPart`** from **`ai`** so you only aggregate **`type: "text"`** segments. Skip **`system`** turns unless you surface them deliberately. Assistant messages can also include **reasoning**, **tool**, **source**, and other part types—extend this loop when you need those in the UI. ```tsx "use client"; import { useChat } from "@ai-sdk/react"; import { DefaultChatTransport, isTextUIPart, type UIMessage } from "ai"; import { Message, MessageStack, MessageContent, MessageMarkdown, } from "@/components/nexus-ui/message"; import { Thread, ThreadContent, ThreadScrollToBottom, } from "@/components/nexus-ui/thread"; function textFromMessage(message: UIMessage) { return message.parts.filter(isTextUIPart).map((p) => p.text).join(""); } export default function ChatThread() { const { messages } = useChat({ transport: new DefaultChatTransport({ api: "/api/chat" }), }); return ( {messages .filter((m) => m.role !== "system") .map((m) => ( {textFromMessage(m)} ))} ); } ```
## API Reference ### Thread Root scroll container wrapping [**`StickToBottom`**](https://github.com/stackblitz/use-stick-to-bottom). **`ThreadContent`** and **`ThreadScrollToBottom`** must live under **`Thread`** so the scrollable list and jump control share the same stick-to-bottom context. number", description: "Optional override to compute scroll top from layout.", }, contextRef: { type: "Ref", description: "Optional ref to the library context object.", }, instance: { type: "StickToBottomInstance", description: "Optional external instance from useStickToBottom.", }, className: { type: "string", description: "Additional CSS classes to apply to the thread root.", }, children: { type: "ReactNode | ((context) => ReactNode)", description: "Typically ThreadContent plus ThreadScrollToBottom; render props receive the library context if needed.", }, }} /> ### ThreadContent Wraps the transcript—usually **`Message`** rows—so **`Thread`** can keep the viewport following the bottom as new content arrives (**`StickToBottom.Content`**). ReactNode)", description: "Message list or other thread body.", }, }} /> ### ThreadScrollToBottom Optional control that appears when the user has scrolled away from the bottom, so they can jump back to the latest messages. Supports polymorphism via **`asChild`**.