a2ui
Use this skill when working with A2UI (Agent-to-User Interface) - Google's open protocol for agent-driven declarative UIs. Triggers on tasks involving A2UI message generation, component catalogs, data binding, surface management, renderer development, custom components, or integrating A2UI with A2A Protocol, AG UI, or agent frameworks like Google ADK. Covers building agents that generate A2UI JSON, setting up client renderers (Lit, React, Angular, Flutter), creating custom catalogs, and handling client-to-server actions.
ai-ml a2uiagent-uideclarative-uigoogle-adka2a-protocolagent-interfacesWhat is a2ui?
Use this skill when working with A2UI (Agent-to-User Interface) - Google's open protocol for agent-driven declarative UIs. Triggers on tasks involving A2UI message generation, component catalogs, data binding, surface management, renderer development, custom components, or integrating A2UI with A2A Protocol, AG UI, or agent frameworks like Google ADK. Covers building agents that generate A2UI JSON, setting up client renderers (Lit, React, Angular, Flutter), creating custom catalogs, and handling client-to-server actions.
a2ui
a2ui is a production-ready AI agent skill for claude-code, gemini-cli, openai-codex, and 1 more. Working with A2UI (Agent-to-User Interface) - Google's open protocol for agent-driven declarative UIs.
Quick Facts
| Field | Value |
|---|---|
| Category | ai-ml |
| Version | 0.1.0 |
| Platforms | claude-code, gemini-cli, openai-codex, mcp |
| License | MIT |
How to Install
- Make sure you have Node.js installed on your machine.
- Run the following command in your terminal:
npx skills add AbsolutelySkilled/AbsolutelySkilled --skill a2ui- The a2ui skill is now available in your AI coding agent (Claude Code, Gemini CLI, OpenAI Codex, etc.).
Overview
A2UI is an open-source protocol from Google that enables AI agents to generate rich, interactive user interfaces through declarative JSON rather than executable code. It solves the critical challenge of safely transmitting UI across trust boundaries in multi-agent systems - agents describe UI intent using a flat list of pre-approved components, and clients render them natively across web, mobile, and desktop. The format is optimized for LLM generation with streaming support, incremental updates, and framework-agnostic rendering.
Tags
a2ui agent-ui declarative-ui google-adk a2a-protocol agent-interfaces
Platforms
- claude-code
- gemini-cli
- openai-codex
- mcp
Related Skills
Pair a2ui with these complementary skills:
Frequently Asked Questions
What is a2ui?
Use this skill when working with A2UI (Agent-to-User Interface) - Google's open protocol for agent-driven declarative UIs. Triggers on tasks involving A2UI message generation, component catalogs, data binding, surface management, renderer development, custom components, or integrating A2UI with A2A Protocol, AG UI, or agent frameworks like Google ADK. Covers building agents that generate A2UI JSON, setting up client renderers (Lit, React, Angular, Flutter), creating custom catalogs, and handling client-to-server actions.
How do I install a2ui?
Run npx skills add AbsolutelySkilled/AbsolutelySkilled --skill a2ui in your terminal. The skill will be immediately available in your AI coding agent.
What AI agents support a2ui?
This skill works with claude-code, gemini-cli, openai-codex, mcp. Install it once and use it across any supported AI coding agent.
Maintainers
Generated from AbsolutelySkilled
SKILL.md
A2UI - Agent-to-User Interface Protocol
A2UI is an open-source protocol from Google that enables AI agents to generate rich, interactive user interfaces through declarative JSON rather than executable code. It solves the critical challenge of safely transmitting UI across trust boundaries in multi-agent systems - agents describe UI intent using a flat list of pre-approved components, and clients render them natively across web, mobile, and desktop. The format is optimized for LLM generation with streaming support, incremental updates, and framework-agnostic rendering.
When to use this skill
Trigger this skill when the user:
- Wants to build an agent that generates A2UI JSON responses
- Needs to set up a client renderer (Lit, React, Angular, Flutter) for A2UI
- Is working with A2UI message types (
surfaceUpdate,createSurface, etc.) - Wants to create or customize an A2UI component catalog
- Needs to implement data binding between components and a data model
- Is integrating A2UI with A2A Protocol or AG UI transport
- Wants to handle client-to-server actions (button clicks, form submissions)
- Is building custom components or extending the basic catalog
Do NOT trigger this skill for:
- General UI framework questions unrelated to agent-generated interfaces
- A2A Protocol questions that don't involve UI rendering
Setup & authentication
Environment variables
GEMINI_API_KEY=your-gemini-api-keyAgent-side installation (Python with Google ADK)
pip install google-adk
adk create my_agentClient-side installation
# Lit (web components)
npm install @a2ui/web-lib lit @lit-labs/signals
# React
npm install @a2ui/react @a2ui/web-lib
# Angular
npm install @a2ui/angular @a2ui/web-lib
# Flutter
flutter pub add flutter_genuiQuickstart (full demo)
git clone https://github.com/google/a2ui.git
cd a2ui
export GEMINI_API_KEY="your_key"
cd samples/client/lit
npm install
npm run demo:allCore concepts
Adjacency list model: A2UI uses a flat list of components with ID references
instead of nested trees. This is easier for LLMs to generate incrementally and
enables progressive rendering. Each component has an id, type, and
properties.
Surfaces: A surface is a UI container identified by surfaceId. Components
and data models are scoped to surfaces. Multiple surfaces can exist independently.
A surface is locked to a catalog for its lifetime.
Data binding: Components connect to application state via JSON Pointer paths (RFC 6901). The data model is separate from UI structure, enabling reactive updates. Input components (TextField, CheckBox) bind bidirectionally.
Catalogs: JSON Schema files defining which components, functions, and themes an agent can use. The Basic Catalog provides standard components. Production apps define custom catalogs matching their design system. Agents can only request pre-approved components from the negotiated catalog.
Two specification versions:
| Version | Status | Key differences |
|---|---|---|
| v0.8 | Stable | surfaceUpdate/dataModelUpdate/beginRendering, nested component syntax, literalString wrappers |
| v0.9 | Draft | createSurface/updateComponents/updateDataModel, flat component syntax, direct strings, required catalogId |
Common tasks
Generate a v0.9 A2UI surface with components
Create a surface, add components, set data, in JSONL format (one JSON per line):
{"version": "v0.9", "createSurface": {"surfaceId": "main", "catalogId": "https://a2ui.org/specification/v0_9/basic_catalog.json"}}
{"version": "v0.9", "updateComponents": {"surfaceId": "main", "components": [
{"id": "header", "component": "Text", "text": "Book Your Table", "variant": "h1"},
{"id": "date-input", "component": "DateTimeInput", "label": "Select Date", "value": {"path": "/reservation/date"}, "enableDate": true},
{"id": "submit-btn", "component": "Button", "child": "btn-text", "variant": "primary", "action": {"event": {"name": "confirm_booking"}}}
]}}
{"version": "v0.9", "updateDataModel": {"surfaceId": "main", "path": "/reservation", "value": {"date": "2025-12-15", "time": "19:00", "guests": 2}}}Generate a v0.8 A2UI surface (legacy)
{"surfaceUpdate": {"surfaceId": "main", "components": [
{"id": "header", "component": {"Text": {"text": {"literalString": "Book Your Table"}, "usageHint": "h1"}}},
{"id": "date-picker", "component": {"DateTimeInput": {"label": {"literalString": "Select Date"}, "value": {"path": "/reservation/date"}, "enableDate": true}}},
{"id": "submit-btn", "component": {"Button": {"child": "submit-text", "action": {"name": "confirm_booking"}}}}
]}}
{"dataModelUpdate": {"surfaceId": "main", "contents": [
{"key": "reservation", "valueMap": [
{"key": "date", "valueString": "2025-12-15"},
{"key": "time", "valueString": "19:00"},
{"key": "guests", "valueInt": 2}
]}
]}}
{"beginRendering": {"surfaceId": "main", "root": "header"}}v0.8 requires
beginRenderingbefore the client renders. v0.9 renders oncreateSurface.
Handle client-to-server actions
Wire a button to dispatch an event with context from the data model:
{
"id": "submit-btn",
"component": "Button",
"child": "btn-text",
"action": {
"event": {
"name": "submit_reservation",
"context": {
"time": {"path": "/reservationTime"},
"size": {"path": "/partySize"}
}
}
}
}Add validation checks that auto-disable the button:
{
"checks": [
{
"condition": {"call": "required", "args": {"value": {"path": "/partySize"}}},
"message": "Party size is required"
}
]
}Build an agent with Google ADK
from google.adk import Agent
import json
import jsonschema
# Load A2UI schema for validation
with open("a2ui_schema.json") as f:
a2ui_schema = json.load(f)
AGENT_INSTRUCTION = """You are a restaurant booking agent.
When the user wants to book, generate A2UI JSON after the delimiter ---a2ui_JSON---
Output a JSON list of A2UI messages using the v0.9 format."""
agent = Agent(
model="gemini-2.5-flash",
name="booking_agent",
instruction=AGENT_INSTRUCTION,
)
# Validate generated A2UI before sending
def validate_a2ui(json_string):
parsed = json.loads(json_string)
jsonschema.validate(instance=parsed, schema=a2ui_schema)
return parsedUse data binding with dynamic lists
Render a list of items from the data model using templates:
{"version": "v0.9", "updateComponents": {"surfaceId": "main", "components": [
{"id": "product-list", "component": "List", "direction": "vertical",
"template": {"dataBinding": "/products", "componentId": "product-card"}},
{"id": "product-card", "component": "Card", "children": ["product-name", "product-price"]},
{"id": "product-name", "component": "Text", "text": {"path": "/name"}},
{"id": "product-price", "component": "Text", "text": {"path": "/price"}}
]}}
{"version": "v0.9", "updateDataModel": {"surfaceId": "main", "path": "/products", "value": [
{"name": "Widget A", "price": "$9.99"},
{"name": "Widget B", "price": "$14.99"}
]}}Inside templates, paths are scoped to each array item (e.g.,
/namerefers to the current item's name).
Set up a Lit web renderer
import { A2uiMessageProcessor } from '@a2ui/web_core/data/model-processor';
import { SurfaceModel } from '@a2ui/web_core/v0_9';
import type * as Types from '@a2ui/web_core/types/types';
// Process incoming JSONL stream
const processor = new A2uiMessageProcessor();
processor.onSurface((surfaceModel: SurfaceModel) => {
// Render the surface using Lit components
renderSurface(surfaceModel);
});
// Feed messages from transport
function handleMessage(jsonLine: string) {
processor.processMessage(JSON.parse(jsonLine));
}Create a custom catalog
{
"catalogId": "https://myapp.com/catalog/v1",
"components": {
"StockTicker": {
"type": "object",
"properties": {
"symbol": {"type": "string"},
"refreshInterval": {"type": "number", "default": 5000}
},
"required": ["symbol"]
}
},
"functions": [],
"theme": {}
}Build with the catalog tool:
uv run tools/build_catalog/assemble_catalog.py \
my_components.json \
--extend-basic-catalog \
--output-name my-catalog \
--catalog-id "https://myapp.com/catalog/v1"Error handling
| Error | Cause | Resolution |
|---|---|---|
VALIDATION_FAILED |
Component properties don't match catalog schema | Check component type exists in catalog and all required properties are set |
| Unknown component type | Agent used a component not in the negotiated catalog | Verify agent prompt includes correct catalog; add component to custom catalog |
| Data binding resolution failure | JSON Pointer path doesn't exist in data model | Send updateDataModel before referencing the path; check for typos in path |
| Surface not found | Operating on a surfaceId that hasn't been created |
Send createSurface (v0.9) or surfaceUpdate (v0.8) first |
| Catalog negotiation failure | No matching catalog between agent and client | Include supportedCatalogIds in client metadata; check agent's advertised catalogs |
Gotchas
v0.8 vs v0.9 syntax is incompatible - The two spec versions use entirely different message shapes (
surfaceUpdatevscreateSurface, nested component syntax vs flat). Mixing them in the same stream will fail silently on most renderers. Confirm the renderer version before generating any output.beginRenderingis mandatory in v0.8 - In v0.8, the client will not render anything until it receives abeginRenderingmessage with therootcomponent ID. Forgetting it causes a blank surface with no error. In v0.9,createSurfacetriggers rendering immediately.Catalog lock-in for the surface lifetime - Once a surface is created with a
catalogId, it cannot be changed. If you reference a component from a different catalog, it will fail validation. Plan the catalog choice before streaming any messages.JSON Pointer paths must pre-exist in the data model - A data binding like
{"path": "/reservation/date"}will fail to resolve if the data model hasn't been populated yet. Always sendupdateDataModel(v0.9) ordataModelUpdate(v0.8) before referencing any path.Adjacency list child references must be IDs, not inline objects - Components reference children by string ID only. Embedding a child component object inline instead of by ID will fail schema validation without a useful error message.
References
For detailed content on specific sub-domains, read the relevant file
from the references/ folder:
references/components.md- full component reference with all types and propertiesreferences/messages.md- complete message format for v0.8 and v0.9references/catalogs-and-actions.md- catalog schema, negotiation, and client-to-server actionsreferences/renderer-guide.md- implementation checklist for building custom renderers
Only load a references file if the current task requires it - they are long and will consume context.
References
catalogs-and-actions.md
Catalogs and Actions
Catalog schema
A catalog is a JSON Schema file defining what components, functions, and themes an agent can use. All A2UI JSON is validated against the chosen catalog.
{
"catalogId": "https://myapp.com/catalog/v1",
"components": {
"Text": {
"type": "object",
"properties": {
"text": {"type": "string"},
"variant": {"type": "string", "enum": ["h1", "h2", "h3", "h4", "h5", "body", "caption"]}
},
"required": ["text"]
},
"StockTicker": {
"type": "object",
"properties": {
"symbol": {"type": "string"},
"refreshInterval": {"type": "number", "default": 5000}
},
"required": ["symbol"]
}
},
"functions": [
{
"name": "openUrl",
"parameters": {
"type": "object",
"properties": {
"url": {"type": "string", "format": "uri"}
},
"required": ["url"]
}
}
],
"theme": {
"primaryColor": {"type": "string"},
"fontFamily": {"type": "string"}
}
}Catalog negotiation (3-step handshake)
- Agent advertises: Agent optionally lists supported catalogs (e.g., in A2A AgentCard)
- Client sends preferences: Include
supportedCatalogIdsin message metadata (required) - Agent selects: Picks best matching catalog when creating a surface; locked for the surface lifetime
// Client metadata in every outgoing message
{
"a2uiClientCapabilities": {
"supportedCatalogIds": [
"https://a2ui.org/specification/v0_9/basic_catalog.json",
"https://myapp.com/catalog/v1"
]
}
}Building custom catalogs
Use the catalog build tool to assemble catalogs from multiple source files:
uv run tools/build_catalog/assemble_catalog.py \
base_components.json \
custom_components.json \
--extend-basic-catalog \
--output-name my-catalog \
--catalog-id "https://myapp.com/catalog/v1" \
--version "1.0.0" \
--out-dir ./catalogsOptions:
--extend-basic-catalog: Include all standard A2UI components--output-name: Output filename (without extension)--catalog-id: URI identifier for the catalog--version: Semantic version string
Catalog versioning
- Any structural change (new component, new property, rename, removal) requires a new version
- Metadata-only changes (descriptions, typos) do not require versioning
- Include version in the catalogId URI:
https://myapp.com/catalog/v2
Two-phase validation
- Agent-side: Validate generated JSON against catalog schema before sending
- Client-side: Validate received messages on receipt
Error reporting:
{
"version": "v0.9",
"error": {
"code": "VALIDATION_FAILED",
"surfaceId": "flight-card-123",
"path": "/components/FlightCard/flightNumber",
"message": "Missing required property 'flightNumber' in component 'FlightCard'."
}
}Client-to-server actions
Server events
Dispatch user actions to the agent for processing:
{
"id": "book-btn",
"component": "Button",
"child": "btn-label",
"action": {
"event": {
"name": "submit_booking",
"context": {
"date": {"path": "/booking/date"},
"guests": {"path": "/booking/guests"}
}
}
}
}The context object resolves data binding paths at click time and includes
resolved values in the action payload sent to the agent.
Local function calls
Execute client-side functions without contacting the agent:
{
"action": {
"functionCall": {
"call": "openUrl",
"args": {"url": "https://example.com/help"}
}
}
}Functions must be defined in the catalog's functions array.
Validation checks
Auto-disable buttons until conditions are met:
{
"id": "submit-btn",
"component": "Button",
"child": "btn-text",
"action": {
"event": {"name": "submit_form"}
},
"checks": [
{
"condition": {"call": "required", "args": {"value": {"path": "/email"}}},
"message": "Email is required"
},
{
"condition": {"call": "required", "args": {"value": {"path": "/name"}}},
"message": "Name is required"
}
]
}Data model sync
Enable with sendDataModel: true in createSurface. The client attaches its
entire data model as a2uiClientDataModel metadata to every outgoing message.
This enables stateless agents that don't need to track form state.
Security considerations
- Sandboxed execution: No arbitrary code runs - only declarative JSON
- Data model isolation: Each surface has its own isolated data model
- Orchestrator responsibility: Strip
a2uiClientDataModelmetadata before forwarding to sub-agents to prevent cross-agent data leakage - Surface ownership: Route actions to the agent that owns the surface via
surfaceId
components.md
A2UI Component Reference
Component categories
Layout components
Row - Horizontal layout container.
| Property | Type | Description |
|---|---|---|
children |
string[] | List of child component IDs |
justify |
string | Horizontal distribution: start, center, end, spaceBetween, spaceAround |
align |
string | Vertical alignment: start, center, end, stretch |
Column - Vertical layout container. Same properties as Row but axes are swapped.
List - Renders a collection of items. Supports static children or dynamic templates.
| Property | Type | Description |
|---|---|---|
children |
string[] | Static child component IDs |
direction |
string | vertical or horizontal |
template |
object | Dynamic rendering: {dataBinding: "/path", componentId: "template-id"} |
Display components
Text - Renders text content.
| Property | Type | v0.8 | v0.9 |
|---|---|---|---|
text |
string/binding | {literalString: "..."} or {path: "/..."} |
"string" or {path: "/..."} |
| Style hint | string | usageHint: h1-h5, body, caption |
variant: h1-h5, body, caption |
Image - Displays an image.
| Property | Type | Description |
|---|---|---|
src |
string/binding | Image URL |
alt |
string | Accessibility text |
fit |
string | cover or contain |
Icon - Renders a named icon from the catalog's icon set.
Divider - Visual separator.
| Property | Type | Description |
|---|---|---|
axis |
string | horizontal or vertical |
Video - Embeds video content with a source URL.
Interactive components
Button - Clickable action trigger.
| Property | Type | v0.8 | v0.9 |
|---|---|---|---|
child |
string | Child component ID for label | Same |
| Style | string | primary flag |
variant: primary |
| Action | object | {name: "event_name"} |
{event: {name: "...", context: {...}}} |
checks |
array | N/A | Validation conditions (v0.9 only) |
TextField - Text input with bidirectional data binding.
| Property | Type | Description |
|---|---|---|
value |
binding | {path: "/data/field"} - reads and writes to data model |
label |
string/binding | Input label text |
textFieldType |
string | text, email, password, number, tel, url |
CheckBox - Boolean toggle with bidirectional binding.
| Property | Type | Description |
|---|---|---|
value |
binding | Path to boolean in data model |
label |
string/binding | Checkbox label |
Slider - Numeric range input.
| Property | Type | Description |
|---|---|---|
value |
binding | Path to numeric value |
min |
number | Minimum value |
max |
number | Maximum value |
step |
number | Step increment |
DateTimeInput - Date and/or time picker.
| Property | Type | Description |
|---|---|---|
value |
binding | Path to date/time string |
label |
string/binding | Input label |
enableDate |
boolean | Show date picker |
enableTime |
boolean | Show time picker |
MultipleChoice / ChoicePicker - Selection from options.
| Property | Type | Description |
|---|---|---|
value |
binding | Path to selected value(s) |
options |
array | Available choices |
Container components
Card - Visual grouping container with optional elevation/border.
| Property | Type | Description |
|---|---|---|
children |
string[] | Child component IDs |
Modal - Overlay dialog triggered by an entry point component.
| Property | Type | Description |
|---|---|---|
entryPoint |
string | Component ID that opens the modal |
content |
string | Component ID rendered inside the modal |
Tabs - Tabbed navigation container.
| Property | Type | Description |
|---|---|---|
tabItems |
array | Tab definitions with labels and content component IDs |
Common properties (all components)
| Property | Type | Description |
|---|---|---|
id |
string | Required. Unique identifier |
component |
string | Required (v0.9). Component type name |
accessibility |
object | Accessibility metadata (label, role) |
weight |
number | Flex weight for layout distribution |
v0.8 vs v0.9 syntax comparison
v0.8 component:
{"id": "title", "component": {"Text": {"text": {"literalString": "Hello"}, "usageHint": "h1"}}}v0.9 component:
{"id": "title", "component": "Text", "text": "Hello", "variant": "h1"}v0.8 children:
{"children": {"explicitList": ["child-1", "child-2"]}}v0.9 children:
{"children": ["child-1", "child-2"]} messages.md
A2UI Message Reference
A2UI uses JSON Lines (JSONL) for streaming - each line is a complete JSON object representing one operation. Messages flow from agent to client via any transport.
v0.9 message types (current draft)
createSurface
Creates a new UI surface. Must be sent before any component or data updates.
{
"version": "v0.9",
"createSurface": {
"surfaceId": "booking-form",
"catalogId": "https://a2ui.org/specification/v0_9/basic_catalog.json",
"sendDataModel": true
}
}catalogIdis required in v0.9sendDataModel: trueenables the client to attach its data model to outgoing messages
updateComponents
Adds or updates components on an existing surface.
{
"version": "v0.9",
"updateComponents": {
"surfaceId": "booking-form",
"components": [
{"id": "title", "component": "Text", "text": "Reservation", "variant": "h1"},
{"id": "name-input", "component": "TextField", "label": "Your Name", "value": {"path": "/guest/name"}},
{"id": "row-1", "component": "Row", "children": ["title", "name-input"], "justify": "spaceBetween"}
]
}
}- Components are upserted by
id- sending a component with an existing ID updates it - Order within the array does not matter; parent-child relationships use ID references
updateDataModel
Sets or updates data in the surface's data model.
{
"version": "v0.9",
"updateDataModel": {
"surfaceId": "booking-form",
"path": "/guest",
"value": {
"name": "Jane Doe",
"email": "jane@example.com"
}
}
}pathuses JSON Pointer (RFC 6901) notation- Granular updates: send only the changed path, not the entire model
- Supports nested paths:
"/guest/preferences/dietary"
deleteSurface
Removes a surface and all its components and data.
{
"version": "v0.9",
"deleteSurface": {
"surfaceId": "booking-form"
}
}error
Report errors back to the agent.
{
"version": "v0.9",
"error": {
"code": "VALIDATION_FAILED",
"surfaceId": "booking-form",
"path": "/components/FlightCard/flightNumber",
"message": "Missing required property 'flightNumber'"
}
}v0.8 message types (stable)
surfaceUpdate
Creates or updates a surface with components. Serves as both create and update.
{
"surfaceUpdate": {
"surfaceId": "main",
"components": [
{"id": "header", "component": {"Text": {"text": {"literalString": "Hello"}, "usageHint": "h1"}}}
]
}
}dataModelUpdate
Sets data in the surface's data model using typed value fields.
{
"dataModelUpdate": {
"surfaceId": "main",
"contents": [
{"key": "user", "valueMap": [
{"key": "name", "valueString": "Jane"},
{"key": "age", "valueInt": 30},
{"key": "active", "valueBool": true}
]}
]
}
}Value types: valueString, valueInt, valueFloat, valueBool, valueMap, valueList.
beginRendering
Signals the client to start rendering. Required in v0.8 before UI appears.
{
"beginRendering": {
"surfaceId": "main",
"root": "header"
}
}rootspecifies the top-level component ID to start the render tree
deleteSurface
Same as v0.9.
Message ordering rules
- Components must be defined before they are referenced as children
createSurface(v0.9) orsurfaceUpdate(v0.8) must come before data/component updates- In v0.8,
beginRenderingmust come after all initial components and data are sent - After initial render, component and data updates can arrive in any order
- Different surfaces operate independently - no ordering constraints between them
Client-to-server action payload (v0.9)
When a user interacts with an action-bearing component:
{
"version": "v0.9",
"action": {
"name": "submit_reservation",
"surfaceId": "booking-form",
"sourceComponentId": "submit-btn",
"timestamp": "2026-02-25T10:40:00Z",
"context": {
"time": "7:00 PM",
"size": 4
}
}
}contextvalues are resolved from data binding paths defined in the component's action- Client includes
a2uiClientCapabilitieswithsupportedCatalogIdsin message metadata
Transport options
| Transport | Status | Notes |
|---|---|---|
| A2A Protocol | Stable | Multi-agent systems, enterprise meshes |
| AG UI | Stable | Full-stack React apps, auto-translates A2UI messages |
| REST/SSE | Planned | Simple HTTP streaming |
| WebSocket | Proposed | Real-time bidirectional |
renderer-guide.md
Renderer Development Guide
Available renderers
| Renderer | Platform | v0.8 | v0.9 | Package |
|---|---|---|---|---|
| Lit | Web | Yes | Yes | @a2ui/web-lib + Lit |
| React | Web | Yes | No | @a2ui/react |
| Angular | Web | Yes | Yes | @a2ui/angular |
| Flutter (GenUI SDK) | Mobile/Desktop/Web | Yes | Yes | flutter_genui |
| SwiftUI | iOS/macOS | - | - | Planned Q2 2026 |
| Jetpack Compose | Android | - | - | Planned Q2 2026 |
All web renderers share @a2ui/web-lib (web_core) as their foundation.
web_core modules
| Module | Import | Purpose |
|---|---|---|
| MessageProcessor | @a2ui/web_core/data/model-processor |
JSONL stream processing, message dispatch |
| v0.9 MessageProcessor | @a2ui/web_core/v0_9 |
v0.9-specific processing |
| SurfaceModel | @a2ui/web_core/v0_9 |
Surface state management |
| SurfaceGroupModel | @a2ui/web_core/v0_9 |
Multi-surface coordination |
| DataModel / DataContext | @a2ui/web_core/data/* |
Data binding resolution, path lookups |
| ComponentModel | @a2ui/web_core/data/* |
Component tree state, adjacency list resolution |
| Types | @a2ui/web_core/types/types |
TypeScript type definitions |
| Primitives | @a2ui/web_core/types/primitives |
Primitive type helpers |
| Styles | @a2ui/web_core/styles/index |
Style resolution utilities |
| Expression parser | @a2ui/web_core/v0_9 |
Client-side function evaluation (v0.9 only) |
Key imports
import type * as Types from '@a2ui/web_core/types/types';
import type * as Primitives from '@a2ui/web_core/types/primitives';
import { A2uiMessageProcessor } from '@a2ui/web_core/data/model-processor';
import { MessageProcessor, SurfaceModel } from '@a2ui/web_core/v0_9';
import * as Styles from '@a2ui/web_core/styles/index';Implementation checklist
Message processing and state
- Surface management keyed by
surfaceId- handle multiple surfaces independently - Component buffering as
Map<string, Component>per surface, store byid - Resolve component references via container properties (children, child, entryPoint)
- Separate data model store per surface
- Handle adjacency list
contentsformat for v0.8 data model
Rendering logic
- Buffer all
surfaceUpdate/dataModelUpdatemessages, wait forbeginRenderingbefore initial render (v0.8) - For v0.9, render on
createSurfacereceipt - Start render from specified
rootcomponent ID (v0.8) or first component (v0.9) - Data binding resolution order: Check
literal*values first, then resolvepathreferences - Support relative binding in dynamic list templates (paths scoped to array item)
- Dynamic lists: Resolve
template.dataBindingpath, iterate data items, rendertemplate.componentIdfor each
Client-to-server communication
- Construct
userActionpayload on user interaction - Resolve
action.contextbindings at interaction time (not at render time) - Include
a2uiClientCapabilitieswithsupportedCatalogIdsin every outgoing A2A message metadata - Send
errormessages for failed data binding or unknown component types
Component mapping
Map A2UI component types to native framework widgets:
// Example component registry
const componentMap: Record<string, ComponentRenderer> = {
'Text': renderText,
'Button': renderButton,
'TextField': renderTextField,
'Card': renderCard,
'Row': renderRow,
'Column': renderColumn,
'List': renderList,
'Image': renderImage,
'CheckBox': renderCheckBox,
'DateTimeInput': renderDateTimeInput,
'Slider': renderSlider,
'Modal': renderModal,
'Tabs': renderTabs,
'Icon': renderIcon,
'Divider': renderDivider,
'Video': renderVideo,
'MultipleChoice': renderMultipleChoice,
};
function renderComponent(model: ComponentModel): FrameworkElement {
const renderer = componentMap[model.type];
if (!renderer) {
console.warn(`Unknown component type: ${model.type}`);
return renderFallback(model);
}
return renderer(model);
}Custom component registration
Extend the renderer with application-specific components:
// Register custom component
componentMap['StockTicker'] = (model: ComponentModel) => {
const symbol = resolveBinding(model.properties.symbol);
const interval = model.properties.refreshInterval ?? 5000;
return createStockTickerWidget(symbol, interval);
};Safety rules for custom components:
- Only register trusted, reviewed components
- Validate all properties before use
- Process user input through sanitization
- Restrict API access from custom component code
Frequently Asked Questions
What is a2ui?
Use this skill when working with A2UI (Agent-to-User Interface) - Google's open protocol for agent-driven declarative UIs. Triggers on tasks involving A2UI message generation, component catalogs, data binding, surface management, renderer development, custom components, or integrating A2UI with A2A Protocol, AG UI, or agent frameworks like Google ADK. Covers building agents that generate A2UI JSON, setting up client renderers (Lit, React, Angular, Flutter), creating custom catalogs, and handling client-to-server actions.
How do I install a2ui?
Run npx skills add AbsolutelySkilled/AbsolutelySkilled --skill a2ui in your terminal. The skill will be immediately available in your AI coding agent.
What AI agents support a2ui?
a2ui works with claude-code, gemini-cli, openai-codex, mcp. Install it once and use it across any supported AI coding agent.