---
name: nexus-ui
description: Install and compose Nexus UI components for AI chat UIs — prompt input, model selector, suggestions, attachments, message, thread, citation, reasoning, text shimmer, image, feedback bar, toaster, chain of thought, tool, questions, and AI SDK patterns. Activates for @nexus-ui registry usage or Nexus UI source under components/nexus-ui.
user-invocable: false
---
# Nexus UI
An open-source component library for building AI-powered interfaces. Composable, copy-paste primitives for chat, streaming, and multimodal experiences — built on React, Tailwind CSS v4, and Radix UI.
Components are distributed via the [shadcn registry](https://ui.shadcn.com/docs/registry) and installed as source code into the user's project.
> **IMPORTANT:** Run CLI commands using the project's package runner: `npx shadcn@latest`, `pnpm dlx shadcn@latest`, or `bunx --bun shadcn@latest` — based on the project's `packageManager`.
## Installation
### 1. Add the registry
Add the Nexus UI registry to the project's `components.json`:
```json
{
"registries": {
"@nexus-ui": "https://nexus-ui.dev/r/{name}.json"
}
}
```
### 2. Add components
```bash
npx shadcn@latest add @nexus-ui/prompt-input
npx shadcn@latest add @nexus-ui/model-selector
npx shadcn@latest add @nexus-ui/suggestions
npx shadcn@latest add @nexus-ui/attachments
npx shadcn@latest add @nexus-ui/message
npx shadcn@latest add @nexus-ui/thread
npx shadcn@latest add @nexus-ui/citation
npx shadcn@latest add @nexus-ui/reasoning
npx shadcn@latest add @nexus-ui/text-shimmer
npx shadcn@latest add @nexus-ui/image
npx shadcn@latest add @nexus-ui/feedback-bar
npx shadcn@latest add @nexus-ui/toaster
npx shadcn@latest add @nexus-ui/chain-of-thought
npx shadcn@latest add @nexus-ui/tool
npx shadcn@latest add @nexus-ui/questions
```
Or install directly via URL (no registry config needed):
```bash
npx shadcn@latest add https://nexus-ui.dev/r/prompt-input.json
```
Components are installed to `components/nexus-ui/` by default.
### Prerequisites
- React 19+
- Tailwind CSS v4
- TypeScript
- shadcn/ui initialized (`npx shadcn@latest init`)
## Available Components
| Component | Registry name | Description |
|-----------|---------------|-------------|
| Prompt Input | `prompt-input` | Composable chat input with auto-resizing textarea and action slots |
| Model Selector | `model-selector` | Dropdown for selecting AI models with radio groups, sub-menus, and custom items |
| Suggestions | `suggestions` | Prompt suggestion chips for guiding user input |
| Attachments | `attachments` | Composable file attachments for chat inputs and messages with preview, variants, and upload wiring |
| Message | `message` | Chat message layout with markdown, optional avatar, actions, and attachments |
| Thread | `thread` | Scrollable chat thread with stick-to-bottom scrolling and jump-to-bottom control |
| Citation | `citation` | Inline source references with hover preview and multi-source carousel |
| Reasoning | `reasoning` | Collapsible model reasoning trace with streaming-aware labels and markdown body |
| Text Shimmer | `text-shimmer` | Animated shimmer text for loading, tool runs, and other in-progress UI |
| Image | `image` | Image renderer for URLs, base64, and byte payloads with preview, loader, lightbox, and action slots |
| Feedback Bar | `feedback-bar` | Feedback prompt bar for per-message or thread ratings with action and close slots |
| Toaster | `toaster` | Headless toast notifications powered by Sonner, with variant-aware styling and custom action/cancel controls |
| Chain of Thought | `chain-of-thought` | Structured multi-step thought timeline with step status, optional expandable output, and auto-close when steps finish |
| Tool | `tool` | State-aware tool call UI with JSON input/output codeblocks (`pending`, `ready`, `running`, `completed`, `error`) |
| Questions | `questions` | Follow-up clarification questions with single or multiple choice, carousel navigation, and batch submission |
## Component APIs
Each section uses the same outline: **Import** → **Parts** → **Root props** → **Props & hooks** → **Usage notes** → **Example** (when a short snippet helps).
### Prompt Input
Composable chat shell: auto-growing textarea plus a bottom action row for attach/send and similar controls.
**Import:**
```tsx
import {
PromptInput,
PromptInputTextarea,
PromptInputActions,
PromptInputActionGroup,
PromptInputAction,
} from "@/components/nexus-ui/prompt-input";
```
**Parts:**
| Part | Purpose |
|------|---------|
| `PromptInput` | Root layout: bordered card wrapping textarea and actions. |
| `PromptInputTextarea` | Auto-resizing textarea (max height ~160px) inside shadcn `ScrollArea`. |
| `PromptInputActions` | Bottom bar; `justify-between` for left vs right action groups. |
| `PromptInputActionGroup` | Horizontal group of actions (`gap-2`). |
| `PromptInputAction` | Slot for one control; supports `asChild` and optional built-in tooltip via `tooltip` (string or object). |
**Root props:** Optional `onSubmit(value: string)` on `PromptInput` for Enter-to-submit from `PromptInputTextarea` (Shift+Enter adds newline), plus normal DOM attributes / `className`.
**Props & hooks:** Every part accepts `className`. `PromptInputTextarea` accepts standard textarea props. `PromptInputAction` supports `asChild` and `tooltip?: string | { content?: string; side?: "top" | "right" | "bottom" | "left"; shortcut?: string }` (no tooltip renders when `content` is missing). There is no context hook.
**Usage notes:** Put attach/tools in the first `PromptInputActionGroup` and send in the second so `justify-between` separates them. Prefer `PromptInputAction asChild` around `Button` rather than using `PromptInputAction` as the clickable element itself.
**Example:**
```tsx
```
### Model Selector
Dropdown for picking a model (or similar value), built on Radix `DropdownMenu` with optional `items` metadata for labels, descriptions, and icons.
**Import:**
```tsx
import {
ModelSelector,
ModelSelectorPortal,
ModelSelectorTrigger,
ModelSelectorContent,
ModelSelectorGroup,
ModelSelectorLabel,
ModelSelectorItemTitle,
ModelSelectorItemDescription,
ModelSelectorItemIcon,
ModelSelectorItemIndicator,
ModelSelectorItem,
ModelSelectorCheckboxItem,
ModelSelectorRadioGroup,
ModelSelectorRadioItem,
ModelSelectorSeparator,
ModelSelectorSub,
ModelSelectorSubTrigger,
ModelSelectorSubContent,
} from "@/components/nexus-ui/model-selector";
```
**Parts:**
| Part | Purpose |
|------|---------|
| `ModelSelector` | Root provider + dropdown root; drives selection context (see **Root props**). |
| `ModelSelectorPortal` | Optional portal wrapper for menu content (use when portaling content). |
| `ModelSelectorTrigger` | Opens the menu; shows the selected label when `items` is set. |
| `ModelSelectorContent` | Menu surface / panel. |
| `ModelSelectorGroup` | Groups rows; often paired with `ModelSelectorLabel`. |
| `ModelSelectorLabel` | Section heading inside a group. |
| `ModelSelectorItem` | Generic menu row. |
| `ModelSelectorItemTitle` | Primary text line for an item. |
| `ModelSelectorItemDescription` | Secondary text line for an item. |
| `ModelSelectorItemIcon` | Leading icon slot for an item. |
| `ModelSelectorItemIndicator` | Selection indicator (e.g. check). |
| `ModelSelectorCheckboxItem` | Checkbox-style menu item. |
| `ModelSelectorRadioGroup` | Single-select group; pass Radix `value` / `onValueChange`. |
| `ModelSelectorRadioItem` | Radio row; supports `title`, `description`, `icon`, `indicator`. |
| `ModelSelectorSeparator` | Visual divider between sections. |
| `ModelSelectorSub` | Nested submenu container. |
| `ModelSelectorSubTrigger` | Row that expands a submenu. |
| `ModelSelectorSubContent` | Popover body for nested items. |
**Root props:** On `ModelSelector`, `value` and `onValueChange` are required. Optional `items`: `{ value, title, description?, icon? }[]` to populate trigger copy and internal metadata. Other Radix menu-root props apply, but `value` / `onValueChange` on the root are reserved for this API.
**Props & hooks:** `ModelSelectorTrigger` adds `variant`: `filled` \| `outline` \| `ghost` (default `filled`). Selection state lives in your React state (`value` / `onValueChange`); there is no extra hook beyond Radix behavior.
**Usage notes:** Prefer `ModelSelectorRadioGroup` + `ModelSelectorRadioItem` for exclusive choices; use `ModelSelectorCheckboxItem` when multiple toggles are needed. Nest advanced trees with `ModelSelectorSub` / `SubTrigger` / `SubContent`. Long menus benefit from `ModelSelectorGroup`, `ModelSelectorLabel`, and `ModelSelectorSeparator`.
**Example:**
```tsx
// models: { value: string; title: string; description?: string; icon?: ComponentType<{ className?: string }> }[]
const [model, setModel] = React.useState(models[0].value);
Select model
{models.map((m) => (
))}
```
### Suggestions
Chips for quick prompts, with an optional animated panel for grouped suggestions.
**Import:**
```tsx
import Suggestions, {
SuggestionList,
Suggestion,
SuggestionPanel,
SuggestionPanelHeader,
SuggestionPanelTitle,
SuggestionPanelClose,
SuggestionPanelContent,
} from "@/components/nexus-ui/suggestions";
```
**Parts:**
| Part | Purpose |
|------|---------|
| `Suggestions` | Root; optional `onSelect(value: string)` for chip clicks (via context). |
| `SuggestionList` | Flex list of chips; `orientation` `"horizontal"` \| `"vertical"`. |
| `Suggestion` | Chip `Button`; `variant` `filled` \| `outline` \| `ghost`; optional `value`, `highlight`. |
| `SuggestionPanel` | Animated shell around grouped suggestions. |
| `SuggestionPanelHeader` | Top bar inside the panel. |
| `SuggestionPanelTitle` | Title row in the header. |
| `SuggestionPanelClose` | Close control; supports `asChild`. |
| `SuggestionPanelContent` | Body region; supports `asChild`. |
**Root props:** `Suggestions` — optional `onSelect(value: string)`. `SuggestionPanel` — optional `open` (default `true`), `onOpenChange`, `onClose` (runs after the exit animation).
**Props & hooks:** No selector hook; wire behavior through `onSelect` and `Suggestion`’s `Button` props. `Suggestion` omits outer `variant` in favor of the Nexus `variant` union above.
**Usage notes:** Combine multiple `SuggestionList` blocks or place lists inside `SuggestionPanel` for progressive disclosure. `highlight` dims matching substrings inside chip labels. Use `SuggestionPanelClose` in the header for dismiss affordances.
**Example:**
```tsx
// Often paired with prompt state: onSelect((q) => setInput(q))
setInput(q)}>
Summarize this thread
Draft a reply
```
### Attachments
Controlled file previews for a prompt or message row: hidden file input, optional window-level drag/drop, and per-item remove/progress UI.
**Import:**
```tsx
import Attachments, {
AttachmentTrigger,
AttachmentList,
Attachment,
AttachmentPreview,
AttachmentRemove,
AttachmentInfo,
AttachmentProperty,
AttachmentProgress,
AttachmentsDropOverlay,
useAttachments,
toAttachmentMeta,
filesFromDataTransfer,
} from "@/components/nexus-ui/attachments";
```
**Parts:**
| Part | Purpose |
|------|---------|
| `Attachments` | Root provider: hidden ``, validation, blob URL lifecycle, optional `window` drop listeners. |
| `AttachmentTrigger` | Opens the picker (pair with `useAttachments().inputId` / labels when building custom triggers). |
| `AttachmentList` | Horizontal scroller / flex row hosting each `Attachment`. |
| `Attachment` | One tile; pass `attachment: AttachmentMeta`; `variant` `compact` \| `inline` \| `detailed` \| `pasted`. |
| `AttachmentPreview` | Thumbnail or type icon region (context: current attachment). |
| `AttachmentInfo` | Text column wrapper for `AttachmentProperty` rows. |
| `AttachmentProperty` | Renders `name`, `size`, or file `kind` via `as="name" \| "size" \| "kind"`. |
| `AttachmentRemove` | Removes the current item (calls `Attachment`’s `onRemove` when provided). |
| `AttachmentProgress` | Thin progress bar; `value` 0–100. |
| `AttachmentsDropOverlay` | Dropshadow overlay during drag; `variant` `fullscreen` \| `contained`. |
**Root props:** On `Attachments`, required `attachments` and `onAttachmentsChange`. Optional: `accept`, `multiple` (default `true`), `maxFiles`, `maxSize` (per file, bytes), `disabled`, `onFileInputChange`, `onFilesRejected`, `windowDrop`.
**Props & hooks:** `useAttachments()` — only under `` — exposes `inputRef`, `inputId`, `openPicker`, `appendFiles`, `isDraggingFile`, current `attachments`, `onAttachmentsChange`, limits, and `disabled`. Exported types: `AttachmentMeta`, `AttachmentsRejectedFiles`, `AppendFilesOptions`. Helpers: `toAttachmentMeta(file, options?)`, `filesFromDataTransfer` for paste/drop.
**Usage notes:** Call `appendFiles` from custom drop/paste handlers to reuse the same limits as the native picker. Enable `windowDrop` plus `AttachmentsDropOverlay` for app-wide file drag hints. Object URLs are revoked when items disappear from the controlled `attachments` array.
**Example:**
```tsx
const [items, setItems] = React.useState([]);
{items.map((meta, index) => (
setItems(items.filter((_, i) => i !== index))}
/>
))}
```
### Message
User vs assistant rows with optional avatar, markdown body (via [Streamdown](https://github.com/vercel/streamdown)), and trailing actions.
**Import:**
```tsx
import {
Message,
MessageStack,
MessageContent,
MessageMarkdown,
MessageActions,
MessageActionGroup,
MessageAction,
MessageAvatar,
} from "@/components/nexus-ui/message";
```
**Parts:**
| Part | Purpose |
|------|---------|
| `Message` | Root; required `from`: `"user"` \| `"assistant"` (layout + `role="article"`). |
| `MessageStack` | Vertical stack for multiple blocks inside one message. |
| `MessageContent` | Bubble / content wrapper (user vs assistant styling). |
| `MessageMarkdown` | Renders markdown with Streamdown plugins (code, math, mermaid, CJK). |
| `MessageActions` | Action row container. |
| `MessageActionGroup` | Groups action buttons. |
| `MessageAction` | Single action; supports `asChild` and optional built-in tooltip via `tooltip` (string or object). |
| `MessageAvatar` | Avatar column; `src`, optional `fallback`, `alt`, `size`, `delayMs`. |
**Root props:** On `Message`, required `from`: `"user"` \| `"assistant"`. Standard `HTMLAttributes` apply to the article wrapper. Default `aria-label` is `"User message"` or `"Assistant message"` unless you pass `aria-label` or `aria-labelledby`.
**Props & hooks:** No exported context hook. Descendants (`MessageStack`, `MessageContent`, `MessageActions`, etc.) read `from` from internal context (fallback `assistant` if used outside `Message`). `MessageMarkdown` accepts [Streamdown](https://github.com/vercel/streamdown) props (`ComponentProps`): e.g. markdown via `children`, optional `components` merged over built-in `code` / `table` / `inlineCode` defaults, plus `className`. `MessageAction` supports `asChild` (Radix `Slot`) and `tooltip?: string | { content?: string; side?: "top" | "right" | "bottom" | "left"; shortcut?: string }` (no tooltip renders when `content` is missing). `MessageAvatar`: required `src`; optional `alt`, `fallback`, `delayMs`, `size`, `className`.
**Usage notes:** Registry install adds `codeblock.tsx` next to `message.tsx`. Merge Streamdown-related CSS from the registry item when installing manually. Prefer `MessageAction asChild` around `Button` for actions.
**Example:**
```tsx
Here is **markdown** and `inline code`.
Short user reply
```
### Thread
Wraps [`use-stick-to-bottom`](https://github.com/stackblitz/use-stick-to-bottom) for a scrollable message list that stays pinned to new content, with a floating control when the user scrolls up.
**Import:**
```tsx
import {
Thread,
ThreadContent,
ThreadScrollToBottom,
} from "@/components/nexus-ui/thread";
```
**Parts:**
| Part | Purpose |
|------|---------|
| `Thread` | Root; forwards `StickToBottom` props (`resize`, `initial`, spring options, etc.). |
| `ThreadContent` | Scrollable column for messages (`flex flex-col gap-*`). |
| `ThreadScrollToBottom` | Button (or `asChild`) shown when not at bottom; calls `scrollToBottom`. |
**Root props:** `Thread` is `StickToBottom` with defaults `resize="smooth"` and `initial="smooth"`. Optional [`StickToBottomOptions`](https://github.com/stackblitz/use-stick-to-bottom): `resize`, `initial`, `mass`, `damping`, `stiffness`, `targetScrollTop`. Also `instance` (pre-created stick-to-bottom instance), `contextRef`, and normal `HTMLAttributes` on the outer div.
**Props & hooks:** Inside `Thread`, use `useStickToBottomContext()` from `use-stick-to-bottom` for `isAtBottom`, `scrollToBottom`, `state`, etc. `ThreadScrollToBottom` uses that context internally and returns `null` when already at the bottom. It accepts `asChild` plus standard button HTML attributes (when not `asChild`, renders a styled `