assistant-ui Elements
Always consult assistant-ui.com/llms.txt for the latest API.
Elements are the styled, copy-into-your-project components that sit on top of the unstyled primitives. Every element is a TSX file the CLI copies into components/assistant-ui/elements/, so you own the source and customize it in place. The catalog at assistant-ui.com/elements documents each one with a live demo, a runtime recipe, and a standalone recipe.
References
- ./references/catalog.md -- every element by section with its registry item and installed file
- ./references/aui-elements.md -- the runtime-connected elements (Thread, ThreadList, AssistantModal, AssistantSidebar, ToolFallback, ToolGroup, Reasoning, Sources, Attachment, ModelSelector, Voice, McpConfig, and the renderers)
- ./references/standalone-elements.md -- props-driven elements grouped by section, with the prop shapes that matter
Two kinds of element
| Kind | File | Needs a runtime | Registry item |
|---|---|---|---|
| Runtime-connected | elements/<name>.aui.tsx |
Yes, reads the nearest AssistantRuntimeProvider |
short name: thread, thread-list, tool-fallback, reasoning, ... |
| Standalone | elements/<name>.tsx |
No, everything comes in through props | elements-<slug>: elements-agent-plan, elements-code-diff, ... |
| Renderer | elements/<name>.tsx |
Used from a .aui element or a toolkit render |
markdown-text, syntax-highlighter, shiki-highlighter, mermaid-diagram, generative-ui |
A few elements exist in both forms. thread-list installs the runtime-connected sidebar list and elements-thread-list installs the props-driven one; the same split applies to reasoning, sources, tool-group, model-selector, message-timing, context-display, voice (the orb), attachment (elements-message-attachment), quote (elements-quote-reply), and follow-up-suggestions (elements-suggestions). Pick .aui when the data is already in the thread; pick standalone when you render from your own state, a tool result, or a design mock.
Install
npx assistant-ui@latest init # once per project: components.json, style, base elements
npx assistant-ui@latest add thread thread-list
npx assistant-ui@latest add elements-agent-plan elements-code-diff
add wraps npx shadcn@latest add with the registry URL, pulls registry dependencies (for example thread brings markdown-text, tool-fallback, tool-group, reasoning, sources, attachment, image, file, follow-up-suggestions, tooltip-icon-button) and npm dependencies (@assistant-ui/react, lucide-react, tw-shimmer). Standalone elements pull elements-surfaces for the shared tokens.
Without a components.json the CLI warns and installs the Base UI flavor. To let the shadcn CLI resolve elements directly, register the style-aware URL:
{
"registries": {
"@assistant-ui": "https://r.assistant-ui.com/styles/{style}/{name}.json"
}
}
Then npx shadcn@latest add @assistant-ui/thread works too. Styles named base-* receive the Base UI flavor; every other style receives Radix. Most elements compose only @assistant-ui/react primitives and ship one source for both; the ones that touch library-specific surfaces (tooltip-icon-button, attachment, context-display, message-timing, mcp-config, threadlist-sidebar, assistant-modal, model-selector, and the shared select, tabs, accordion, badge, direction) have a dedicated Base UI variant. The plain https://r.assistant-ui.com/{name}.json URL stays a Radix fallback.
Use a runtime-connected element
"use client";
import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime } from "@assistant-ui/ai-sdk";
import { Thread } from "@/components/assistant-ui/elements/thread.aui";
import { ThreadList } from "@/components/assistant-ui/elements/thread-list.aui";
export default function Chat() {
const runtime = useChatRuntime();
return (
<AssistantRuntimeProvider runtime={runtime}>
<div className="flex h-dvh">
<ThreadList />
<Thread />
</div>
</AssistantRuntimeProvider>
);
}
Thread takes no runtime prop; it reads the nearest provider. It renders the welcome screen, a history skeleton while a switched thread loads, the message list, the scroll-to-bottom pill, follow-up suggestions, and the composer.
Override a slot instead of forking the file
import { Thread, type ThreadComponents } from "@/components/assistant-ui/elements/thread.aui";
const THREAD_COMPONENTS: ThreadComponents = {
Welcome: () => <h1 className="text-2xl">Ask me anything</h1>,
ToolFallback: MyToolFallback,
};
export function Chat() {
return <Thread components={THREAD_COMPONENTS} autoFocus={false} />;
}
Slots: AssistantMessage, Welcome, ToolFallback, ToolGroup, ReasoningGroup. Define the object at module scope or memoize it, otherwise every parent render remounts the message subtrees. For per-tool UI, register render on the toolkit entry (see tools) rather than overriding ToolFallback; a tool UI registered by name wins over the slot.
Restyle
Thread sets --thread-max-width, --composer-bg, --composer-radius, and --composer-padding inline on ThreadPrimitive.Root, so change them by editing your copy of thread.aui.tsx, not from an outer class. Everything else is Tailwind on the copied file.
Use a standalone element
import { AgentPlan } from "@/components/assistant-ui/elements/agent-plan";
<AgentPlan
steps={["Read the schema", "Write the migration", "Run the tests"]}
activeIndex={1}
/>;
Standalone elements take plain props (here steps: readonly string[] and activeIndex: number, plus any div props) and never read runtime state, so they work in a tool render, in a dashboard, or in Storybook. Each catalog page's "Standalone" tab shows the exact prop shape; the standalone reference summarizes the ones you will reach for most.
Shared surface tokens
elements-surfaces installs components/assistant-ui/elements/surfaces.tsx, a set of class-string constants and two helpers that give every element the same look: paper and floating (card and popover fills), field and fieldInteractive (inputs), pressable, ghostButton, inkButton, mono, live (the streaming accent), iconSwap and labelSwap transitions, collapsePanel, codeScroll and codeSurface, plus ShimmerLabel and SwapLabel. Reuse them in your own components with cn(...) from @/lib/utils (the CLI scaffolds lib/utils.ts if it is missing) so custom UI matches the catalog.
Common Gotchas
Cannot find module '@/components/assistant-ui/thread'
- Elements moved under
elements/and runtime-connected files carry the.auisuffix:@/components/assistant-ui/elements/thread.aui. Renderers and standalone elements have no suffix.
Element renders but looks unstyled
- The registry assumes Tailwind v4 and the shadcn theme variables in
globals.css(@import "tailwindcss"plus the--background,--foreground,--borderfamily).npx assistant-ui@latest initwrites them; in a hand-rolled project copy them from acreatescaffold.
CLI installed the wrong flavor
addreads thestylefield ofcomponents.json;base-*styles get Base UI, anything else gets Radix. Fix the style, then re-add with--overwrite.
Thread re-renders every message on each parent render
- The
componentsprop object was created inline. Hoist it to module scope oruseMemo.
A .aui element throws about a missing provider
- It must be a descendant of
AssistantRuntimeProvider(or anAuiProviderwhose config supplies the scope it reads). Standalone elements have no such requirement.
Registry item not found
- Runtime-connected and renderer items use the short name (
thread,markdown-text); standalone items are prefixed (elements-agent-plan). The catalog lists the exact item per element.
Related Skills
- primitives -- the unstyled building blocks the
.auielements compose - setup --
create,init, and the rest of the CLI - tools -- toolkit
renderentries that replaceToolFallbackper tool - generative-ui -- the
generative-uirenderer and the component vocabulary - markdown --
markdown-text,shiki-highlighter,syntax-highlighter,mermaid-diagram - thread-list --
thread-list,threadlist-sidebar,thread-search,conversation-map