assistant-ui Primitives

SkillCommunication

Guides your agent in building custom chat interfaces using assistant-ui components like threads, composers, and message views.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the assistant-ui Primitives skill

About this capability

Guide for assistant-ui UI primitives - ThreadPrimitive, ComposerPrimitive, MessagePrimitive. Use when customizing chat UI components.

What this skill tells your AI

The instructions your AI receives, as published by compozy/compozy in .agents/skills/assistant-ui/assistant-ui-primitives/SKILL.md and read by ahel’s review.

Always consult assistant-ui.com/llms.txt for latest API.

Composable, unstyled components following Radix UI patterns.

References

Import

import {
  AuiIf,
  ThreadPrimitive,
  ComposerPrimitive,
  MessagePrimitive,
  ActionBarPrimitive,
  BranchPickerPrimitive,
  AttachmentPrimitive,
  ThreadListPrimitive,
  ThreadListItemPrimitive,
} from "@assistant-ui/react";

Primitive Parts

PrimitiveKey Parts
ThreadPrimitive.Root, .Viewport, .Messages, .Empty, .ScrollToBottom
ComposerPrimitive.Root, .Input, .Send, .Cancel, .Attachments
MessagePrimitive.Root, .Parts/.Content, .If, .Error
ActionBarPrimitive.Copy, .Edit, .Reload, .Speak, .FeedbackPositive, .FeedbackNegative, .ExportMarkdown
BranchPickerPrimitive.Previous, .Next, .Number, .Count

Custom Thread Example

function CustomThread() {
  return (
    <ThreadPrimitive.Root className="flex flex-col h-full">
      <ThreadPrimitive.Empty>
        <div className="flex-1 flex items-center justify-center">
          Start a conversation
        </div>
      </ThreadPrimitive.Empty>

      <ThreadPrimitive.Viewport className="flex-1 overflow-y-auto p-4">
        <ThreadPrimitive.Messages components={{
          UserMessage: CustomUserMessage,
          AssistantMessage: CustomAssistantMessage,
        }} />
      </ThreadPrimitive.Viewport>

      <ComposerPrimitive.Root className="border-t p-4 flex gap-2">
        <ComposerPrimitive.Input className="flex-1 rounded-lg border px-4 py-2" />
        <ComposerPrimitive.Send className="bg-blue-500 text-white px-4 py-2 rounded-lg">
          Send
        </ComposerPrimitive.Send>
      </ComposerPrimitive.Root>
    </ThreadPrimitive.Root>
  );
}

Conditional Rendering

Prefer AuiIf for new code. Primitive .If components still exist but are deprecated.

<AuiIf condition={({ message }) => message.role === "user"}>
  User only
</AuiIf>
<AuiIf condition={({ thread }) => thread.isRunning}>
  Generating...
</AuiIf>
<AuiIf condition={({ message }) => message.branchCount > 1}>
  Has edit history
</AuiIf>

<AuiIf condition={({ thread }) => thread.isRunning}>
  <ComposerPrimitive.Cancel>Stop</ComposerPrimitive.Cancel>
</AuiIf>

<AuiIf condition={({ thread }) => thread.isEmpty}>No messages</AuiIf>

Content Parts

<MessagePrimitive.Content components={{
  Text: ({ part }) => <p>{part.text}</p>,
  Image: ({ part }) => <img src={part.image} alt="" />,
  ToolCall: ({ part }) => <div>Tool: {part.toolName}</div>,
  Reasoning: ({ part }) => <details><summary>Thinking</summary>{part.text}</details>,
}} />

Branch Picker

<MessagePrimitive.If hasBranches>
  <BranchPickerPrimitive.Root className="flex items-center gap-1">
    <BranchPickerPrimitive.Previous>←</BranchPickerPrimitive.Previous>
    <span><BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count /></span>
    <BranchPickerPrimitive.Next>→</BranchPickerPrimitive.Next>
  </BranchPickerPrimitive.Root>
</MessagePrimitive.If>

Common Gotchas

Primitives not rendering

  • Wrap in AssistantRuntimeProvider
  • Ensure parent primitive provides context

Styles not applying

  • Primitives are unstyled by default
  • Add className and style with your app's Tailwind/CSS system

Signals

GitHub stars
3k
Forks
177
Last commit
Sep 2026

ahel recommends instead

Advanced
Catalog kind
skill
Gateway key
assistant-ui-primitives
Source
github.com/compozy/compozy