UIX Components — AI Chat UI in 3 Lines
You are an expert in the UIX protocol and component library. UIX is an AI-to-UI Intermediate Representation (IR) protocol layer that bridges multiple AI protocols into a unified component system.
Core Concept
AI Agent Events (Vercel AI SDK / AG-UI / Anthropic / ...)
↓ adapter
UIX IR (LucidConversation[])
↓ render
React Components (AgentChat)
Quick Start — The 3-Line Pattern
The fastest way to build an AI chat UI:
With Vercel AI SDK
import { AgentChat } from '@uix-ai/agent'
import { useVercelChat } from '@uix-ai/adapter-vercel/react'
export default function Chat() {
const { conversations, status, send, stop } = useVercelChat({ api: '/api/chat' })
return <AgentChat conversations={conversations} status={status} onSend={send} onStop={stop} />
}
With AG-UI Protocol
import { AgentChat } from '@uix-ai/agent'
import { useAGUI } from '@uix-ai/adapter-agui/react'
export default function Chat() {
const { conversations, status, send, stop } = useAGUI({ url: '/api/agent' })
return <AgentChat conversations={conversations} status={status} onSend={send} onStop={stop} />
}
Without Any Adapter (Manual IR)
import { AgentChat } from '@uix-ai/agent'
import type { LucidConversation } from '@uix-ai/core'
const conversations: LucidConversation[] = [
{ id: '1', role: 'user', status: 'completed', blocks: [{ id: 'b1', type: 'text', status: 'completed', content: { text: 'Hello' } }] },
{ id: '2', role: 'assistant', status: 'completed', blocks: [{ id: 'b2', type: 'text', status: 'completed', content: { text: 'Hi! How can I help?' } }] },
]
export default function Chat() {
return <AgentChat conversations={conversations} onSend={(msg) => console.log(msg)} />
}
UIX IR Type System
LucidConversation
interface LucidConversation {
id: string
role: 'user' | 'assistant' | 'system'
status: 'streaming' | 'completed' | 'error'
blocks: LucidBlock[]
agentName?: string
agentAvatar?: string
timestamp?: number
}
LucidBlock
interface LucidBlock {
id: string
type: 'text' | 'tool' | 'thinking' | 'image' | 'file' | 'error' | 'source'
status: 'streaming' | 'completed' | 'error'
content: TextBlockContent | ToolBlockContent | ThinkingBlockContent | ...
}
Block Types
| Type | Content | Description |
|---|
text | { text: string } | Markdown text, supports streaming |
tool | { name, input?, output?, toolStatus } | Tool call with status lifecycle |
thinking | { text: string } | AI reasoning/chain-of-thought |
image | { url, alt?, width?, height? } | Image content |
file | { url, filename, mimeType?, size? } | File attachment |
error | { message, code? } | Error display |
source | { type, url?, title?, excerpt? } | Citation/RAG source |
Type Guards
import { isTextBlock, isToolBlock, isThinkingBlock, isStreaming, isCompleted } from '@uix-ai/core'
if (isTextBlock(block)) {
// block.content is TextBlockContent
}
if (isStreaming(conversation)) {
// show streaming indicator
}
AgentChat Props
interface AgentChatProps {
// Required
conversations: LucidConversation[]
onSend: (message: string) => void
// Optional - status & control
status?: 'idle' | 'streaming' | 'error'
onStop?: () => void
onRetry?: (conversationId: string) => void
// Optional - agent info
agent?: { name: string; avatar?: string; description?: string }
// Optional - tool approval
onToolApprove?: (blockId: string) => void
onToolDeny?: (blockId: string) => void
// Optional - customization
placeholder?: string
emptyState?: React.ReactNode
renderBlock?: (block: LucidBlock) => React.ReactNode | null
showHeader?: boolean
autoScroll?: boolean
}
Composable Components
When you need more control than AgentChat, use individual components:
Layout
| Component | Description |
|---|
ChatWindow | Container with header, messages, input |
ChatWindowHeader | Agent name, avatar, status |
ChatWindowMessages | Scrollable message area |
ChatWindowInput | Text input with send button |
ChatWindowEmpty | Empty state placeholder |
ChatList | Sidebar conversation list |
Messages
| Component | Description |
|---|
ChatMessage | Full message with avatar and blocks |
MessageList | Scrollable message container |
StreamText | Streaming markdown renderer |
ThinkingIndicator | Pulsing thinking state |
ToolResult | Tool call display with status |
SourceBlock | Citation/RAG source card |
Avatars
| Component | Description |
|---|
Avatar | Composable avatar (Avatar > AvatarImage + AvatarFallback) |
AvatarGroup | Stacked avatar group with overflow |
Input
| Component | Description |
|---|
ChatInput | Text input with @ mention support |
MentionPopover | Mention suggestion popup |
Adapter Reference
Vercel AI SDK Adapter
// React hook (recommended)
import { useVercelChat } from '@uix-ai/adapter-vercel/react'
const { conversations, status, send, stop, setMessages } = useVercelChat({
api: '/api/chat',
// all useChat options supported
})
// Manual conversion
import { fromVercelMessages, toVercelMessages } from '@uix-ai/adapter-vercel'
const lucidConversations = fromVercelMessages(vercelMessages)
const vercelMessages = toVercelMessages(lucidConversations)
AG-UI Adapter
// React hook (recommended)
import { useAGUI } from '@uix-ai/adapter-agui/react'
const { conversations, status, send, stop, reset } = useAGUI({
url: '/api/agent',
threadId: 'optional-thread-id',
headers: { Authorization: 'Bearer ...' },
})
// Manual event processing
import { AGUIEventProcessor } from '@uix-ai/adapter-agui'
const processor = new AGUIEventProcessor()
processor.process(event) // feed AG-UI events
const conversations = processor.getConversations()
A2UI Adapter (Experimental)
import { fromA2UIPayload, toA2UIPayload } from '@uix-ai/adapter-a2ui'
// Google A2UI → UIX IR
const conversation = fromA2UIPayload(a2uiPayload)
// UIX IR → Google A2UI
const payload = toA2UIPayload(conversation)
Common Patterns
Next.js App Router + Vercel AI SDK
// app/api/chat/route.ts
import { streamText } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'
export async function POST(req: Request) {
const { messages } = await req.json()
const result = streamText({
model: anthropic('claude-sonnet-4-20250514'),
messages,
})
return result.toDataStreamResponse()
}
// app/page.tsx
'use client'
import { AgentChat } from '@uix-ai/agent'
import { useVercelChat } from '@uix-ai/adapter-vercel/react'
export default function Page() {
const { conversations, status, send, stop } = useVercelChat({ api: '/api/chat' })
return (
<div className="h-screen">
<AgentChat
conversations={conversations}
status={status}
onSend={send}
onStop={stop}
agent={{ name: 'Claude', description: 'AI Assistant' }}
/>
</div>
)
}
Custom Block Rendering
<AgentChat
conversations={conversations}
onSend={send}
renderBlock={(block) => {
if (block.type === 'tool' && block.content.name === 'weather') {
return <WeatherCard data={block.content.output} />
}
return null // fall back to default rendering
}}
/>
Multiple Conversations (Sidebar + Chat)
import { ChatList, ChatWindow } from '@uix-ai/agent'
function App() {
const [activeId, setActiveId] = useState<string>()
return (
<div className="flex h-screen">
<ChatList conversations={allConversations} activeId={activeId} onSelect={setActiveId} />
<ChatWindow conversation={activeConversation} onSend={send} />