A2UI Overview
A2UI is an open standard for agent-driven user interfaces. It lets an AI agent describe a structured UI — components, layout, and live data — using a simple JSON protocol, and have that UI rendered automatically inside a chat session.
When an agent response begins with the ---a2ui_JSON--- sentinel, the chat switches to A2UI rendering mode automatically — provided you supply a component catalog via the views input.
The implementation follows the A2UI v0.9 specification. For the full protocol reference, see a2ui.org.
How It Works in ChatComponent
When content arrives with the ---a2ui_JSON--- prefix, the streaming pipeline switches to JSONL mode. Each newline-delimited JSON object is parsed as an A2UI message and handed to the surface store.
The surface store maintains a Map<string, A2uiSurface> keyed by surface ID. Each surface holds a flat component map, a data model, theme metadata, and the catalog ID used to resolve components.
To render A2UI surfaces, pass a ViewRegistry to the ChatComponent via the views input. The a2uiBasicCatalog() function provides the 18 built-in components.
The Four Message Types
The A2UI protocol is built on four message types. Agents compose entire interfaces by sending sequences of these messages.
| Message | Purpose |
|---|---|
createSurface | Creates a new named surface with a catalog ID and optional theme |
updateComponents | Adds or replaces components on a surface (merged by component ID) |
updateDataModel | Sets or patches a value in the surface data model via JSON Pointer |
deleteSurface | Removes a surface and all its components |
createSurface
Declares a new surface. Must be sent before any other message for that surface ID.
updateComponents
Sends one or more component definitions. Each component has a unique id, a component type name, and any component-specific props.
updateDataModel
Sets values at a JSON Pointer path in the surface's data model. Components whose props reference these paths re-render automatically.
Omit path (or use /) to replace the entire data model at once.
deleteSurface
Removes a surface from the store and dismisses its rendered output.
Action-Event Bridge
A2UI actions (button clicks, form events) now map directly to render-spec on bindings. When surfaceToSpec() converts a surface to a spec, each component's action prop is translated into an on binding on the corresponding spec element. This means A2UI actions flow through the render-lib event system rather than being handled ad-hoc by individual components.
The bridge provides two default handlers:
a2ui:event-- dispatches named events (e.g.,submit,cancel) back to the agenta2ui:localAction-- executes local function calls (e.g.,openUrl)
This unified event flow means consumers can observe all A2UI interactions through the RenderEvent stream on RenderSpecComponent, including handler execution, state changes, and lifecycle signals. See the Render Events page for the full event type reference.
A2UI vs json-render
Both A2UI and json-render produce rendered UIs inside chat messages, but they serve different purposes.
| A2UI | json-render | |
|---|---|---|
| Protocol | Multi-message JSONL stream | Single JSON object |
| State | Surfaces with live data models | Stateless spec |
| Interactivity | Two-way bindings, button actions, validation | Read-only display |
| Detection | ---a2ui_JSON--- prefix | First character is { |
| Use case | Agent-driven dynamic UIs | Rich content cards |
Use A2UI when the agent needs to build an interface incrementally, bind it to live data, or respond to user interactions. Use json-render for rich one-shot content cards — formatted results, structured data displays, and similar read-only output.
Quick Setup
Pass a2uiBasicCatalog() to the views input to enable A2UI rendering:
When the AI response starts with ---a2ui_JSON---, the chat renders the surfaces using the provided catalog.
To extend or replace the built-in components, compose catalogs with withViews() or views(). See the Custom Catalogs guide for details.
Automatic Event Routing
When an A2UI button fires an a2ui:event action, ChatComponent automatically
routes it back to the agent as a human message. No manual (renderEvent) wiring
is needed for standard A2UI event flows.
The event is sent as a JSON-encoded human message:
The renderEvent output still fires for all events, so consumers can observe
or log events without intercepting the routing.
Minimal Consumer Setup
Custom Function Call Handlers
When an A2UI button has a functionCall action, the call name is looked up in the [handlers] map on ChatComponent. This lets you define client-side behavior triggered by agent-built UI:
The agent sends a button with {"action": {"functionCall": {"call": "addToCart", "args": {"sku": "ABC"}}}}. When clicked, the addToCart handler runs in Angular's injection context — inject() works for accessing services.
If no consumer handler matches the call name, built-in handlers are used as fallbacks (e.g., openUrl opens a URL in a new tab).
Handler return values are emitted on the RenderHandlerEvent — observe them via the renderEvent output on ChatComponent.
Validation
A2UI v0.9 uses CheckRule objects for client-side validation. Input components and buttons can define a checks array — each check has a condition (a DynamicBoolean) and an error message.
CheckRule Shape
The condition can be:
- A boolean literal:
trueorfalse - A path reference:
{ "path": "/agreed" }— resolves to a data model value - A FunctionCall:
{ "call": "required", "args": { ... } }— invokes a named function - A composite:
{ "call": "and", "args": { "values": [...] } }— combines multiple conditions
Built-in Functions
| Category | Functions |
|---|---|
| Validation | required, regex, length, numeric, email |
| Logic | and, or, not |
| Formatting | formatString, formatNumber, formatCurrency, formatDate, pluralize |
| Navigation | openUrl |
Input Component Behavior
Input components (TextField, CheckBox, ChoicePicker, Slider, DateTimeInput) validate continuously — errors display inline as the user interacts. The input border changes color to indicate validation state.
Button Behavior
Per the v0.9 spec: if any check fails, the button is automatically disabled. Error messages display below the button.
Composite Conditions
Use and, or, and not to compose complex validation rules:
Custom Catalog Components
Custom catalog components receive a pre-computed validationResult prop:
Use the shared A2uiValidationErrorsComponent for consistent error display:
Theming
Validation styling uses CSS custom properties:
| Property | Default | Usage |
|---|---|---|
--a2ui-error | #ef4444 | Error text and invalid border color |
--a2ui-border | rgba(255,255,255,0.1) | Default input border |
--a2ui-input-bg | rgba(255,255,255,0.05) | Input background |
--a2ui-label | rgba(255,255,255,0.6) | Label text color |
Events & Data Model Transport
When a user triggers an event action (e.g., clicking a button with action.event), the Angular renderer builds a v0.9-compliant action message and sends it back to the agent. Local actions (action.functionCall) execute client-side only — the agent never sees them.
Action Message Shape
The outbound message follows the v0.9 spec:
| Field | Description |
|---|---|
version | Always "v0.9" |
action.name | The event name from the component's action.event.name |
action.surfaceId | The surface that owns this component |
action.sourceComponentId | The id of the component that triggered the event |
action.timestamp | ISO 8601 timestamp of when the action was dispatched |
action.context | Resolved values from action.event.context — path refs and function calls are evaluated against the current data model |
Context Resolution
Context values in action.event.context are DynamicValues — they can be literals, path references, or function calls. They are resolved at dispatch time against the current data model:
When the user clicks the button, the renderer resolves /name and /email from the data model and calls formatCurrency on /amount, producing a flat context object with concrete values.
sendDataModel
Set sendDataModel: true on createSurface to attach the full data model snapshot to every outbound action:
When enabled, the action message includes a metadata field:
The data model is only sent with event actions — there are no passive change notifications on input changes. This matches the v0.9 spec requirement that the data model piggybacks on outbound messages.
Angular Integration
A2uiSurfaceComponent exposes two outputs:
| Output | Type | Description |
|---|---|---|
(action) | A2uiActionMessage | Agent-bound action messages — the complete v0.9 envelope |
(events) | RenderEvent | All render events (state changes, handler calls, lifecycle) for observation |
ChatComponent auto-routes (action) events to the agent as human messages. For standalone usage, bind (action) directly:
Data Model Bindings
When the agent sets component properties using path references ({ "path": "/name" }), the surface component
tracks these as bindings — a mapping from prop name to JSON Pointer path. These bindings are passed to
catalog components as the _bindings prop.
How Bindings Work
- Agent sends components with path references:
{ "value": { "path": "/form/name" } } surfaceToSpecresolves the path to a current value AND records the binding in_bindings- Catalog component reads the resolved value normally. When the user changes the value, it emits an
a2ui:datamodelevent via theemitcallback - The event format is
a2ui:datamodel:{path}:{value}
Using emitBinding
Custom catalog components can use the emitBinding utility for consistent binding emission:
Known Limitations
The current binding mechanism is client-side only — the a2ui:datamodel events are emitted
but do not yet flow through the render lib's StateStore. Data model updates from user input
are not reflected back to other components in real time. Full StateStore integration is planned
for a future release.
Data model state is refreshed when the agent sends an updateDataModel message.
What's Next
API reference for A2uiSurfaceComponent — render a surface outside ChatComponent.
API reference for createA2uiSurfaceStore() and the apply/surfaces/surface interface.
Reference for all 18 built-in components and their props.
How the streaming pipeline detects and routes A2UI content.