docs/concepts/components.md
A2UI uses an adjacency list model for component hierarchies. Instead of nested JSON trees, components are a flat list with ID references.
Traditional nested approach:
A2UI adjacency list:
=== "v0.8"
```json
{
"surfaceUpdate": {
"components": [
{
"id": "root",
"component": {
"Column": {
"children": { "explicitList": ["greeting", "buttons"] }
}
}
},
{
"id": "greeting",
"component": {
"Text": { "text": { "literalString": "Hello" } }
}
},
{
"id": "buttons",
"component": {
"Row": {
"children": { "explicitList": ["cancel-btn", "ok-btn"] }
}
}
},
{
"id": "cancel-btn",
"component": {
"Button": {
"child": "cancel-text",
"action": { "name": "cancel" }
}
}
},
{
"id": "cancel-text",
"component": {
"Text": { "text": { "literalString": "Cancel" } }
}
},
{
"id": "ok-btn",
"component": {
"Button": {
"child": "ok-text",
"action": { "name": "ok" }
}
}
},
{
"id": "ok-text",
"component": {
"Text": { "text": { "literalString": "OK" } }
}
}
]
}
}
```
=== "v0.9"
```json
{
"version": "v0.9",
"updateComponents": {
"surfaceId": "main",
"components": [
{
"id": "root",
"component": "Column",
"children": ["greeting", "buttons"]
},
{
"id": "greeting",
"component": "Text",
"text": "Hello"
},
{
"id": "buttons",
"component": "Row",
"children": ["cancel-btn", "ok-btn"]
},
{
"id": "cancel-btn",
"component": "Button",
"child": "cancel-text",
"action": { "event": { "name": "cancel" } }
},
{
"id": "cancel-text",
"component": "Text",
"text": "Cancel"
},
{
"id": "ok-btn",
"component": "Button",
"child": "ok-text",
"action": { "event": { "name": "ok" } }
},
{
"id": "ok-text",
"component": "Text",
"text": "OK"
}
]
}
}
```
v0.9 uses a flatter component format: `"component": "Text"` instead of nested `{"Text": {...}}`, and children are simple arrays instead of `{"explicitList": [...]}`.
Components reference children by ID, not by nesting.
Every component has:
"welcome")Text, Button, Card)=== "v0.8"
```json
{
"id": "welcome",
"component": {
"Text": {
"text": { "literalString": "Hello" },
"usageHint": "h1"
}
}
}
```
=== "v0.9"
```json
{
"id": "welcome",
"component": "Text",
"text": "Hello",
"variant": "h1"
}
```
A2UI defines a standard catalog of components organized by purpose:
For the complete component gallery with examples, see Component Reference.
Static (explicitList) - Fixed list of child IDs:
{
"children": {
"explicitList": ["back-btn", "title", "menu-btn"]
}
}
Dynamic (template) - Generate children from data array:
{
"children": {
"template": {
"dataBinding": "/items",
"componentId": "item-template"
}
}
}
For each item in /items, render the item-template. See Data Binding for details.
Components get their values two ways:
{"text": {"literalString": "Welcome"}}{"text": {"path": "/user/name"}}LLMs can generate components with literal values or bind them to data paths for dynamic content.
Components compose into surfaces (widgets):
=== "v0.8"
1. LLM generates component definitions via `surfaceUpdate`
2. LLM populates data via `dataModelUpdate`
3. LLM signals render via `beginRendering`
4. Client renders all components as native widgets
=== "v0.9"
1. LLM creates a surface via `createSurface` (specifying catalog)
2. LLM generates component definitions via `updateComponents`
3. LLM populates data via `updateDataModel`
4. Client renders all components as native widgets
A surface is a complete, cohesive UI (form, dashboard, chat, etc.).
children list to exclude removed IDsThe flat structure makes all updates simple ID-based operations.
Beyond the standard catalog, clients can define custom components for domain-specific needs:
Custom components are advertised from the client's renderer to the LLM. The LLM can then use them in addition to the standard catalog.
See Custom Components Guide for implementation details.
"user-profile-card" not "c1"