A2UI v0.8 is the original release of the A2UI protocol, designed specifically for AI agents that use structured output mode — a feature offered by many LLM APIs that constrains generation to a strict JSON schema. Although v0.8 served as the foundation for the protocol’s core concepts (surfaces, adjacency-list component trees, data model separation), it has been superseded by the prompt-first v0.9 family and is now in legacy maintenance mode.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/a2ui-project/a2ui/llms.txt
Use this file to discover all available pages before exploring further.
Design philosophy
v0.8 was purpose-built around three requirements:- Structured-output compatibility — every message conforms to a strict JSON schema that can be fed directly to LLM structured-output APIs, eliminating the risk of malformed JSON.
- Progressive rendering — the renderer buffers component and data messages, then renders once
beginRenderingis received, preventing a flash of incomplete content. - Platform agnosticism — the server sends abstract component names (e.g.,
"Text","Row"); the client maps them to native widgets via aWidgetRegistry.
Message types
v0.8 defines four server-to-client message types. Each message is a JSON object with exactly one of the following top-level keys.surfaceUpdate
The primary message for defining UI structure. Contains a flat list of component objects. A surface is implicitly created if it does not yet exist when surfaceUpdate is received.
Properties:
| Property | Type | Required | Description |
|---|---|---|---|
surfaceId | string | — | The target surface ID. Defaults to a global surface if omitted. |
components | array | ✅ | Flat list of component instance objects. |
dataModelUpdate
Updates the data model for a surface. Unlike v0.9.x which uses plain JSON values, v0.8 uses a typed adjacency list (contents array) where each entry carries an explicit typed value key: valueString, valueNumber, valueBoolean, or valueMap.
Properties:
| Property | Type | Required | Description |
|---|---|---|---|
surfaceId | string | — | The target surface ID. |
path | string | — | Path within the data model (e.g., "user"). Defaults to root. |
contents | array | ✅ | Array of { key, value* } entries. |
beginRendering
Signals that the renderer has received enough information to perform the initial render. The renderer buffers all surfaceUpdate and dataModelUpdate messages until this message arrives, preventing partial UI display.
Properties:
| Property | Type | Required | Description |
|---|---|---|---|
surfaceId | string | ✅ | The surface to begin rendering. |
root | string | ✅ | The id of the root component. |
catalogId | string | — | The catalog to use. Defaults to the v0.8 standard catalog. |
In v0.9.x,
beginRendering was eliminated. The createSurface message replaced it, and the renderer renders progressively as soon as the root component is defined — no explicit render signal is needed.deleteSurface
Instructs the renderer to remove a surface and all its components and data. This message is structurally the same across all A2UI versions.
Component format: nested object style
The most visible difference between v0.8 and v0.9.x is how component types are expressed. In v0.8, thecomponent field is a wrapper object with exactly one key — the component type string — whose value contains the component’s properties.
- v0.8 format (nested object)
- v0.9.1 format (flat string)
"component": "Text" format in v0.9.
Data binding: BoundValue
In v0.8, any property that accepts dynamic data uses a BoundValue object with explicit named value keys, rather than a unified Dynamic* type:
BoundValue key | Type | Description |
|---|---|---|
literalString | string | Static string value. |
literalNumber | number | Static number value. |
literalBoolean | boolean | Static boolean value. |
literalArray | array | Static array value. |
path | string | JSON Pointer to a data model location. |
path and a literal* value are present in the same BoundValue, it serves as an initialization shorthand: the renderer writes the literal value into the data model at the given path (as an implicit dataModelUpdate) and then binds the component property to that path.
updateDataModel message, making data flow explicit and unambiguous.
Children: explicitList and template
Container components (Row, Column, List) in v0.8 define their children using a children object with exactly one of two keys:
explicitList— static ordered list of child component IDstemplate— dynamic list rendering from a data-bound array
ChildList type unifies this into a single property: an array value is an explicit list; an object value with path and componentId is a template.
Complete JSONL stream example
The following example renders a minimal user profile card in v0.8 format:Key differences: v0.8 vs. v0.9.1
| Aspect | v0.8 | v0.9.1 |
|---|---|---|
| LLM generation mode | Structured output (schema-constrained) | Prompt-first (schema in prompt) |
| Surface initialization | beginRendering (explicit render signal) | createSurface (render as soon as root defined) |
| Component type syntax | { "Text": { "text": {...} } } (nested object) | "component": "Text" (flat string) |
| Data model update | Typed adjacency list (contents array with valueString, etc.) | Plain JSON value at a JSON Pointer path |
| Data binding | BoundValue with explicit literalString / path keys | DynamicString — bare string or { "path": "..." } |
| Children definition | { "explicitList": [...] } or { "template": {...} } wrapper object | Direct array or { "path": ..., "componentId": ... } object |
| Schema modularity | Single monolithic server_to_client.json with catalog inlined | Separate common_types.json, server_to_client.json, catalog.json |
| Catalog ID in message | Set in beginRendering.catalogId | Set in createSurface.catalogId |
| Client-side validation | Not supported | checks array with named functions |
Migration guide: v0.8 → v0.9.1
Replace surfaceUpdate + beginRendering with createSurface + updateComponents
Remove
beginRendering messages entirely. Replace surfaceUpdate with updateComponents. Add a preceding createSurface message that declares the surfaceId and catalogId. The renderer will begin rendering progressively as soon as it processes the root component — no explicit render signal is needed.Flatten the component object
Change the
component field from a nested discriminated object to a plain string type name, and move all properties to the top-level component object.Replace dataModelUpdate contents with plain JSON values
Replace the typed
contents adjacency list with a value field containing a plain JSON object or scalar. Use a JSON Pointer path to target specific locations within the data model.Update data binding syntax
Replace
BoundValue objects using literalString / literalNumber / path with the simplified DynamicString / DynamicNumber format: either a bare literal or { "path": "..." }.Update children syntax
Replace the
{ "explicitList": [...] } and { "template": {...} } child wrappers with the unified ChildList format.Move catalogId to createSurface
Remove
catalogId from beginRendering (which is now deleted) and add it to the new createSurface message.Client-to-server messages in v0.8
v0.8 client-to-server messages use the keyuserAction (renamed to action in v0.9.x):
action (the outer key and the property name are unified), and the context field is directly a resolved JSON object rather than an array of key/value entries.
Related pages
v0.9.1 Specification
Current stable release — the recommended target for all migration work.
v1.0 Specification
Release candidate with
actionResponse RPC and single-message UI instantiation.Basic Catalog Implementation Guide
Component-by-component guide to implementing the v0.9.1 Basic Catalog.
Transport Bindings
A2A, AG-UI, MCP, and other supported transport layers.