Skip to content
Adaptersassistant-ui adapterMenu

assistant-ui adapter

Keep your assistant-ui runtime and render its messages with chatcn text, reasoning and tool components, approvals included.

Markdown
On this page
An assistant-ui runtime with chatcn text, reasoning and tool parts, approvals wired.

Installation

$ pnpm dlx shadcn@latest add https://chatcn.dev/r/chatcn-assistant-ui.json

Usage

tsx
"use client"

import { MessagePrimitive } from "@assistant-ui/react"
import { toolBashRenderers } from "@/components/ui/chatcn/tool-bash"
import { toolFileEditRenderers } from "@/components/ui/chatcn/tool-file-edit"
import { createChatcnPartComponents } from "@/lib/chatcn-assistant-ui"

// Module scope: new component identities would remount every part.
const components = createChatcnPartComponents({
  renderers: { ...toolBashRenderers, ...toolFileEditRenderers },
})

export function AssistantMessage() {
  return (
    <MessagePrimitive.Root className="flex flex-col gap-3">
      <MessagePrimitive.Parts components={components} />
    </MessagePrimitive.Root>
  )
}

Your runtime, composer, thread list and tool UIs stay as they are. Tools with their own makeAssistantToolUI keep it; every other tool renders through chatcn (tools.Fallback).

What each part becomes

assistant-ui partchatcn
textMarkdown, streaming while the part status is running
reasoningReasoning, redacted when it finishes empty
tool-callThe renderer for its name from renderers, or the generic tool renderer
approval on a tool callAllow and Deny, answered with respondToApproval. Always allow picks the request's allow-always option when it has one
interrupt (human input)Allow and Deny, answered with resume({ approved, reason })
status incomplete, reason cancelledUnfinished calls render as cancelled

Actions for blocks

tsx
import { useChatcnAssistantUiActions } from "@/lib/chatcn-assistant-ui"

// Inside AssistantRuntimeProvider
const actions = useChatcnAssistantUiActions()
actions.send?.("Run the tests")
actions.approve?.(approvalId)
ChatcnActionsassistant-ui
send(text, { files })aui.thread.append with text, image and file parts
stop()aui.thread.cancelRun()
regenerate(messageId?)aui.thread.message({ id }).reload(), the last assistant message by default
approve(id), deny(id, reason)part.respondToToolApproval, or part.resumeToolCall for a human interrupt
submitToolOutput(toolCallId, output)part.addToolResult(output)
editMessage(id, text)aui.thread.append with the edited message as sourceId, which branches

Converting messages

tsx
import { useAuiState } from "@assistant-ui/react"
import { MessageParts } from "@/components/ui/chatcn/message-parts"
import { toChatcnMessage } from "@/lib/chatcn-assistant-ui"

// chatcn's own part renderer, grouping and thinking indicator included.
const message = useAuiState((s) => s.message)
<MessageParts message={toChatcnMessage(message)} streaming={message.status?.type === "running"} />

toChatcnMessage maps text, reasoning, tool calls, sources, files, images, data parts and attachments. toChatcnToolPart maps a single tool call, for your own tool UI.

Notes

  • Verified against @assistant-ui/react 0.15.30.
  • Leaf components never import this adapter; only your app does.