Data binding connects UI components to application state using JSON Pointer paths (RFC 6901). It allows A2UI to efficiently define layouts for large arrays of data and to show updated content without regenerating it from scratch.
A2UI separates:
- UI Structure (Components): What the interface looks like
- Application State (Data Model): What data it displays
This enables:
- Reactive updates.
- Data-driven UIs.
- Reusable templates.
- Bidirectional binding.
Each surface has a JSON object holding state:
{
"user": {"name": "Alice", "email": "alice@example.com"},
"cart": {
"items": [{"name": "Widget", "price": 9.99, "quantity": 2}],
"total": 19.98
}
}Syntax:
/user/name- Object property/cart/items/0- Array index (zero-based)/cart/items/0/price- Nested path
Example:
{"user": {"name": "Alice"}, "items": ["Apple", "Banana"]}/user/name→"Alice"/items/0→"Apple"
=== "v0.8"
**Literal (fixed):**
```json
{
"id": "title",
"component": {
"Text": {
"text": { "literalString": "Welcome" }
}
}
}
```
**Data-bound (reactive):**
```json
{
"id": "username",
"component": {
"Text": {
"text": { "path": "/user/name" }
}
}
}
```
=== "v0.9"
**Literal (fixed):**
```json
{
"id": "title",
"component": "Text",
"text": "Welcome"
}
```
**Data-bound (reactive):**
```json
{
"id": "username",
"component": "Text",
"text": { "path": "/user/name" }
}
```
When /user/name changes from "Alice" to "Bob", the text automatically updates to "Bob".
Components bound to data paths automatically update when the data changes:
{
"id": "status",
"component": {
"Text": {
"text": { "path": "/order/status" }
}
}
}- Initial:
/order/status= "Processing..." → displays "Processing..." - Update: Send a data model update with
status: "Shipped"→ displays "Shipped"
No component updates needed—just data updates.
Use templates to render arrays:
{
"id": "product-list",
"component": {
"Column": {
"children": {
"template": {
"dataBinding": "/products",
"componentId": "product-card"
}
}
}
}
}Data:
{
"products": [
{ "name": "Widget", "price": 9.99 },
{ "name": "Gadget", "price": 19.99 }
]
}Result: Two cards rendered, one per product.
Inside a template, paths are scoped to the array item:
{
"id": "product-name",
"component": {
"Text": {
"text": { "path": "/name" }
}
}
}- For
/products/0,/nameresolves to/products/0/name→ "Widget" - For
/products/1,/nameresolves to/products/1/name→ "Gadget"
Adding/removing items automatically updates the rendered components.
Interactive components update the data model bidirectionally:
| Component | Example | User Action | Data Update |
|---|---|---|---|
| TextField | {"text": {"path": "/form/name"}} |
Types "Alice" | /form/name = "Alice" |
| CheckBox | {"value": {"path": "/form/agreed"}} |
Checks box | /form/agreed = true |
| MultipleChoice | {"selections": {"path": "/form/country"}} |
Selects "Canada" | /form/country = ["ca"] |
-
Use granular updates: Update only changed paths.
{ "dataModelUpdate": { "path": "/user", "contents": [ { "key": "name", "valueString": "Alice" } ] } } -
Organize by domain: Group related data.
{"user": {...}, "cart": {...}, "ui": {...}} -
Pre-compute display values: Formats data (currency, dates) on the agent before sending.
{"price": "$19.99"} // Not: {"price": 19.99}