Prerequisites
Before diving into A2UI, you should have:
- Basic understanding of JSON
- Familiarity with UI component concepts (buttons, forms, inputs)
- Optional: Experience with web development (helpful but not required)
If you haven't already, read our Introduction to A2UI to understand the core concepts.
Understanding A2UI Components
Every A2UI component is defined as a JSON object with these key properties:
type— The component type from the approved catalog (e.g., "a2ui.Button")id— A unique identifier for this component instanceprops— Configuration options specific to the component typechildren— (Optional) Array of child component IDsevents— (Optional) Event handlers for user interactions
Here's the simplest possible A2UI component — a button:
{
"type": "a2ui.Button",
"id": "my-button",
"props": {
"label": "Click Me",
"variant": "primary"
}
}The Adjacency List Format
Unlike traditional nested JSON, A2UI uses a flat adjacency list. Instead of nesting children inside parents, each component is a separate object, and parent-child relationships are expressed through ID references.
This design has several benefits:
- LLM-friendly: Easier for language models to generate correctly
- Streaming support: Components can be sent and rendered incrementally
- Validation: Each component can be validated independently
- Flexibility: Components can be referenced by multiple parents (shared components)
Your First A2UI Message
Let's build something practical: a contact form with name and email inputs. Here's the complete A2UI message:
[
{
"type": "a2ui.Form",
"id": "contact-form",
"children": ["name-input", "email-input", "submit-button"],
"props": {
"onSubmit": "handleContactSubmit"
}
},
{
"type": "a2ui.TextInput",
"id": "name-input",
"props": {
"label": "Your Name",
"placeholder": "Enter your name",
"required": true
}
},
{
"type": "a2ui.TextInput",
"id": "email-input",
"props": {
"label": "Email Address",
"placeholder": "you@example.com",
"type": "email",
"required": true
}
},
{
"type": "a2ui.Button",
"id": "submit-button",
"props": {
"label": "Send Message",
"variant": "primary",
"type": "submit"
}
}
]Let's break down what's happening:
- The
a2ui.Formcomponent defines the container and lists its children by ID - Two
a2ui.TextInputcomponents provide the input fields - An
a2ui.Buttonwithtype: "submit"triggers form submission - The
onSubmitprop tells the client what action to trigger when the form is submitted
Streaming JSONL
One of A2UI's most powerful features is streaming support. Instead of waiting for the entire UI to be generated, clients can render components as they arrive:
// Stream 1: Container arrives
{"type": "a2ui.Container", "id": "root", "children": ["header", "list"]}
// Stream 2: Header arrives
{"type": "a2ui.Text", "id": "header", "props": {"text": "Search Results"}}
// Stream 3: List arrives (can render immediately)
{"type": "a2ui.List", "id": "list", "children": ["item-1", "item-2"]}
// Stream 4-5: Items arrive
{"type": "a2ui.ListItem", "id": "item-1", "props": {"title": "Result 1"}}
{"type": "a2ui.ListItem", "id": "item-2", "props": {"title": "Result 2"}}Each line is a valid JSON object that can be parsed and rendered immediately. The client maintains a map of component IDs and updates the UI as new components arrive.
Adding Interactivity
A2UI components can define event handlers that trigger actions when users interact with them:
{
"type": "a2ui.Button",
"id": "action-button",
"props": {
"label": "Confirm Order",
"variant": "primary"
},
"events": {
"onClick": {
"action": "agent.confirmOrder",
"payload": {
"orderId": "12345"
}
}
}
}When the user clicks this button, the client will:
- Capture the click event
- Look up the "onClick" handler
- Send the action ("agent.confirmOrder") and payload back to the agent
- The agent can then process the order and respond with a new UI
Best Practices
Keep Components Simple
Each component should do one thing well. If you find yourself adding too many props, consider splitting into multiple components.
Use Semantic Types
Choose component types that match your intent. Use a2ui.Button for actions, a2ui.Link for navigation, and a2ui.TextInput for data entry.
Plan for Streaming
Design your UI so that important content can be shown first. Put critical information in early components and progressive details in later ones.
Test on Multiple Renderers
If your agent will be used across platforms, test your A2UI output with different renderers to ensure consistency.
Next Steps
You now have the foundation to start building A2UI components. Here's where to go next:
- Explore the official A2UI documentation for the complete component catalog
- Check out the GitHub repository for renderer implementations and examples
- Read our comparison with MCP Apps to understand when to use each protocol