DataTable
A data table bound to a collection in the data model. Columns are declared; rows live in data and update without re-sending the component.
rendered, live
the wire — click a line to scrub
the contract — DataTable in catalog.json
{
"type": "object",
"description": "A data table bound to a collection in the data model. Columns are declared; rows live in data and update without re-sending the component.",
"properties": {
"component": {
"const": "DataTable"
},
"columns": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"description": "Property name to read from each row object."
},
"label": {
"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString",
"description": "Column header. Required — it is the column's accessible name."
},
"align": {
"type": "string",
"enum": [
"start",
"center",
"end"
],
"default": "start",
"description": "Logical alignment (RTL-safe). Numbers usually read best with 'end'."
},
"format": {
"type": "string",
"enum": [
"text",
"number",
"datetime"
],
"default": "text",
"description": "How to interpret cell values: 'number' formats in the user's locale with tabular figures; 'datetime' expects an ISO 8601 string and renders it in the user's locale."
},
"sortable": {
"type": "boolean",
"default": false,
"description": "Enables client-side sorting on this column. No round trip."
}
},
"required": [
"key",
"label"
],
"additionalProperties": false
},
"description": "Flat column descriptors; each names a key present in the row objects."
},
"rows": {
"oneOf": [
{
"type": "array",
"items": {
"type": "object"
}
},
{
"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DataBinding"
}
],
"description": "The row objects. Bind with {\"path\": ...} so rows and individual cells can update via updateDataModel."
},
"label": {
"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString",
"description": "Optional table caption, e.g. \"Today's deploys\"."
},
"emptyText": {
"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString",
"description": "Shown when rows resolves to an empty array. Keep it quiet and factual."
},
"rowAction": {
"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Action",
"description": "Fired when the user activates a row. The renderer adds `row` (the row object) and `rowIndex` to the action context automatically."
},
"footer": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"description": "Column key this aggregate is computed over and rendered under."
},
"aggregate": {
"enum": [
"sum",
"mean",
"count"
],
"description": "Computed client-side from the resolved rows and recomputed on every updateDataModel — filter the rows and the total follows."
},
"label": {
"type": "string",
"description": "Optional caption rendered with the value, e.g. 'Total ARR at risk'."
}
},
"required": [
"key",
"aggregate"
],
"additionalProperties": false
},
"description": "Aggregate footer row. Values are computed client-side from the resolved rows — never emit a precomputed total; it can silently disagree with the rows it sits under."
}
},
"required": [
"component",
"columns",
"rows"
]
}the prompt-pack — paste into your agent
### DataTable — rows bound to data
Columns are declared on the component; rows live in the data model and update without re-sending
the component — including single cells (`"path": "/deploys/1/status"`).
| prop | type | required | notes |
| ----------- | ------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `columns` | array of columns | yes | see column shape below |
| `rows` | array \| `{"path"}` | yes | bind it — rows are data, not components |
| `label` | string | no | table caption, e.g. `"Today's deploys"` |
| `emptyText` | string | no | shown when rows is empty; quiet and factual |
| `rowAction` | action | no | fired on row activation; `row` + `rowIndex` are added to its context automatically |
| `footer` | array of aggregates | no | totals row, **computed client-side from the rows** — never emit a precomputed total; it can silently disagree with the rows it sits under |
Footer cell shape — `key` (a column key) and `aggregate` (`sum | mean | count`) required;
`label` optional. Aggregates recompute on every `updateDataModel` — filter the rows, the total
follows:
```
{"key":"arr","aggregate":"sum","label":"Total ARR at risk"}
```
Column shape — `key` and `label` required:
```
{"key":"durationSec","label":"Duration","align":"end","format":"number","sortable":true}
```
`align`: `start | center | end` (logical, RTL-safe; numbers read best `end`). `format`:
`text | number | datetime` — `datetime` expects an ISO 8601 string and renders in the user's
locale, so emit `"2026-08-16T14:02:11Z"`, never `"2:02 PM"`.
```
{"id":"deploys","component":"DataTable","label":"Today's deploys","columns":[{"key":"service","label":"Service"},{"key":"status","label":"Status"},{"key":"startedAt","label":"Started","format":"datetime"}],"rows":{"path":"/deploys"},"emptyText":"No deploys yet today.","rowAction":{"event":{"name":"view_deploy"}}}
```