Render A2UI protocol messages using shadcn/ui components.
Work in Progress: This library is under active development and APIs may change. We welcome feedback, bug reports, and contributions! Please open an issue if you encounter any problems or have suggestions.
A2UI is an open-source protocol that enables AI agents to generate rich, interactive user interfaces safely across platforms. This library provides React components to render A2UI messages with beautiful, accessible shadcn/ui components.
- 13 Component Renderers: Row, Column, Text, Image, Icon, Divider, Button, TextField, Checkbox, Card, Modal, Tabs, List
- Streaming Support: Parse JSONL streams for real-time UI updates
- Data Binding: Two-way data binding with reactive updates
- Pluggable Store: Bring your own state management (Zustand, Jotai, Redux) or use the default
- Type-Safe: Full TypeScript support with comprehensive types
- Accessible: Built on Radix UI primitives with ARIA support
- Themeable: Full shadcn/ui theming with Tailwind CSS
npm install a2ui-reactRequires Tailwind CSS v4+. This library uses Tailwind v4 CSS syntax and features.
Add the theme import to your main CSS file:
/* Import the a2ui-react theme (includes Tailwind, theme variables, and source scanning) */
@import 'a2ui-react/theme.css';This single import provides:
- Tailwind CSS base styles
- shadcn/ui theme variables and alert color tokens
- Automatic scanning of the package for Tailwind class usage (required for v4)
import { A2UIProvider, A2UISurface, shadcnRenderers } from 'a2ui-react'
function App() {
const messages = [
{
surfaceUpdate: {
surfaceId: 'main',
updates: [
{ id: 'root', component: { type: 'Column', children: ['title', 'button'] } },
{ id: 'title', component: { type: 'Text', content: 'Hello A2UI!', style: 'h1' } },
{ id: 'button', component: { type: 'Button', primary: true, child: 'btn-text' } },
{ id: 'btn-text', component: { type: 'Text', content: 'Click Me' } },
],
},
},
{ beginRendering: { surfaceId: 'main', root: 'root' } },
]
return (
<A2UIProvider renderers={shadcnRenderers}>
<A2UISurface surfaceId="main" messages={messages} />
</A2UIProvider>
)
}The a2ui-react package includes everything you need:
- Core A2UI types, message parser, and store interface
- React bindings, hooks, and component registry
- shadcn/ui component renderers
- Row: Horizontal flex container with distribution/alignment
- Column: Vertical flex container with distribution/alignment
- Text: Semantic text (h1-h5, body, caption)
- Image: Images with loading state
- Icon: lucide-react icons
- Divider: Horizontal/vertical separators
- Button: Primary/outline variants with action handlers
- TextField: Text, number, date, password, textarea inputs
- Checkbox: Boolean with label and data binding
- Card: Styled card container
- Modal: Dialog with trigger/content
- Tabs: Tabbed interface
- List: Scrollable list container
Create custom renderers by implementing the A2UIRenderer interface:
import type { A2UIRenderer } from 'a2ui-react'
const CustomRenderer: A2UIRenderer = {
type: 'CustomComponent',
render: ({ component, children, data, onAction }) => {
return <div>{children}</div>
},
example: {
name: 'Custom',
description: 'My custom component',
category: 'display',
messages: [...],
},
}Register with the provider:
<A2UIProvider renderers={[...shadcnRenderers, CustomRenderer]}>
{/* ... */}
</A2UIProvider>import { useDataBinding, useAction, useSurface } from 'a2ui-react'
// Two-way data binding
const { value, setValue } = useDataBinding('surfaceId', 'user.name')
// Dispatch actions
const dispatch = useAction()
dispatch({ type: 'submit', payload: { ... } })
// Subscribe to surface updates
const surface = useSurface('surfaceId')# Install dependencies
npm install
# Build all packages
npm run build
# Run tests
npm test
# Start example app
npm run dev
# Lint and format
npm run lint
npm run formatpackages/
├── core/ # Zero-dependency types and parser
├── react/ # React bindings and hooks
├── shadcn/ # shadcn/ui renderers
└── example/ # Interactive simulator app
- React 19.2.3
- TypeScript 5.9.3
- Vite 7.3.0
- TailwindCSS 4.1.18
- Radix UI primitives
- lucide-react icons
- Vitest for testing
- Biome for linting/formatting
- Turborepo for monorepo management
The id must be present both in the update wrapper AND inside the component object:
// ✅ Correct
{
id: 'greeting',
component: {
type: 'Text',
id: 'greeting', // Required inside component
content: 'Hello!'
}
}
// ❌ Won't work - missing id inside component
{
id: 'greeting',
component: {
type: 'Text',
content: 'Hello!'
}
}Works out of the box with a2ui-go - both use the A2UI v0.9 message format.
MIT