maddhruv/a2ui-react

A2UI Implementation in React

★ 0Forks 0TypeScriptGitHub ↗Compare

README

        ___    ____  __  ______   __ __ _ __
       /   |  /__ / / / / /  _/  / //_/(_) /_
      / /| | / __/ / / / // /   / ,<  / / __/
     / ___ |/ /__ / /_/ // /   / /| |/ / /_
    /_/  |_|\___/ \____/___/  /_/ |_/_/\__/

    Headless A2UI for React - Zod-first, tree-shakeable

A2UI React

A React renderer for Google's A2UI (Agent-to-User Interface) protocol - enabling AI agents to generate rich, interactive UIs through declarative JSON.

Built with React, TypeScript, Next.js, Tailwind CSS, and Shadcn/UI.

What is A2UI?

A2UI is an open protocol by Google that allows AI agents to generate user interfaces through declarative JSON rather than executable code. Key features:

  • Declarative JSON - Agents describe UI intent, not executable code
  • Safe by design - Agents can only use pre-approved components from a catalog
  • Streaming-first - JSONL format enables progressive rendering
  • Framework-agnostic - The spec works with React, Lit, Angular, Flutter, etc.

This repo provides a React implementation of the A2UI v0.9 spec with a full demo app.

Quick Start

git clone https://github.com/maddhruv/a2ui-react.git
cd a2ui-react
npm install
npm run dev

Open http://localhost:3000 to see the landing page. Try:

  • /playground - Interactive JSON editor with live A2UI rendering
  • /examples - Form, cards, and streaming examples with mock agents
  • /docs - Concepts, component catalog, and action system docs

Architecture

Agent (Server)                    Renderer (Client)
+-----------------+              +------------------------+
| Generate A2UI   |   JSONL      | Parse JSONL stream     |
| JSON messages   | -----------> | Manage surface state   |
| (any LLM agent) |   stream     | Resolve component tree |
+-----------------+              | Render with Shadcn/UI  |
       ^                         | Handle data binding    |
       |          Action events  | Dispatch actions       |
       +------------------------ +------------------------+

Message Format (v0.9)

Messages stream as JSONL (one JSON object per line):

{"version":"v0.9","createSurface":{"surfaceId":"main","catalogId":"https://a2ui.org/specification/v0_9/basic_catalog.json"}}
{"version":"v0.9","updateDataModel":{"surfaceId":"main","path":"/form","value":{"name":""}}}
{"version":"v0.9","updateComponents":{"surfaceId":"main","components":[
  {"id":"root","component":"Column","children":["title","input"]},
  {"id":"title","component":"Text","text":"Hello","variant":"h2"},
  {"id":"input","component":"TextField","label":"Name","value":{"path":"/form/name"}}
]}}

Component Model (Adjacency List)

A2UI uses a flat list with ID references instead of nested trees. This is optimized for LLM generation - models can emit components incrementally without tracking nesting depth:

[
  {"id": "root", "component": "Column", "children": ["title", "btn"]},
  {"id": "title", "component": "Text", "text": "Hello"},
  {"id": "btn", "component": "Button", "child": "btn-text"},
  {"id": "btn-text", "component": "Text", "text": "Click me"}
]

Usage

import { A2UIProvider, A2UISurface, useA2UI } from "@/lib/a2ui";

function App() {
  return (
    <A2UIProvider onAction={async (event) => {
      // Send action to your agent, return Response for more A2UI messages
      return fetch("/api/agent", { method: "POST", body: JSON.stringify(event) });
    }}>
      <AgentUI />
    </A2UIProvider>
  );
}

function AgentUI() {
  const { surfaceIds, connect } = useA2UI();

  useEffect(() => {
    connect("/api/agent/form");
  }, [connect]);

  return (
    <>
      {surfaceIds.map((id) => (
        <A2UISurface key={id} surfaceId={id} />
      ))}
    </>
  );
}

Supported Components

All 18 Basic Catalog components are implemented with Shadcn/UI:

Component Description Shadcn Mapping
Text Display text with variants (h1-h5, body, caption) Native HTML elements
Button Clickable with action dispatch Shadcn Button
TextField Text input with two-way data binding Shadcn Input / Textarea
CheckBox Toggle with two-way binding Shadcn Checkbox
ChoicePicker Dropdown select Shadcn Select
Slider Range input Shadcn Slider
DateTimeInput Date/time picker Native date/time input
Image Display image Native img
Icon Lucide icon by name Lucide React
Row Horizontal flex div with flex-row
Column Vertical flex div with flex-col
List List with optional templates div with flex
Card Container with title/subtitle Shadcn Card
Tabs Tabbed interface Shadcn Tabs
Modal Dialog with trigger Shadcn Dialog
Divider Separator line Shadcn Separator
Video Video player Native video
AudioPlayer Audio player Native audio

Key Concepts

Data Binding

Components bind to a per-surface data model via JSON Pointer paths (RFC 6901):

{"id": "greeting", "component": "Text", "text": {"path": "/user/name"}}

Input components support two-way binding - user changes update the data model immediately.

Actions

Button actions fire events with context resolved from the data model:

{
  "action": {
    "event": {
      "name": "submit",
      "context": {
        "name": {"path": "/form/name"},
        "email": {"path": "/form/email"}
      }
    }
  }
}

Custom Components

Override or extend the component registry:

<A2UIProvider registryOverrides={{ Text: MyCustomText }}>
  ...
</A2UIProvider>

Project Structure

src/
  lib/a2ui/           # Renderer library
    types/             # TypeScript types (messages, components, data model, actions)
    core/              # Core logic (JSON pointer, data store, surface manager, registry)
    hooks/             # React hooks (useA2UI, useSurface, useDataBinding, useAction)
    context/           # React context provider
    components/        # Shadcn component implementations (18 components)
    stream/            # JSONL stream parser
  app/                 # Next.js demo app
    api/agent/         # Mock agent API routes
    playground/        # Interactive JSON editor + renderer
    examples/          # Form, cards, streaming examples
    docs/              # Documentation pages

Testing

npm test         # Run tests
npm run test:watch  # Watch mode

Tests cover:

  • JSON Pointer (RFC 6901) get/set/remove operations
  • DataStore reactive state management
  • SurfaceManager component lifecycle and root computation
  • MessageProcessor message routing

License

MIT

Contributors

maddhruv

Issues