docs/quickstart.md
Get hands-on with A2UI by running the restaurant finder demo. This guide will have you experiencing agent-generated UI in less than 5 minutes.
By the end of this quickstart, you'll have:
Before you begin, make sure you have:
⚠️ Security Notice
This demo runs an A2A agent that uses Gemini to generate A2UI responses. The agent has access to your API key and will make requests to Google's Gemini API. Always review agent code before running it in production environments.
git clone https://github.com/google/a2ui.git
cd a2ui
Export your Gemini API key as an environment variable:
export GEMINI_API_KEY="your_gemini_api_key_here"
cd samples/client/lit
Run the one-command demo launcher:
npm install
npm run demo:all
This command will:
http://localhost:5173✅ Demo Running
If everything worked, you should see the web app in your browser. The agent is now ready to generate UI!
In the web app, try these prompts:
┌─────────────┐ ┌──────────────┐ ┌────────────────┐
│ You Type │────────>│ A2A Agent │────────>│ Gemini API │
│ a Message │ │ (Python) │ │ (LLM) │
└─────────────┘ └──────────────┘ └────────────────┘
│ │
│ Generates A2UI JSON │
│<────────────────────────┘
│
│ Streams JSONL messages
v
┌──────────────┐
│ Web App │
│ (A2UI Lit │
│ Renderer) │
└──────────────┘
│
│ Renders native components
v
┌──────────────┐
│ Your UI │
└──────────────┘
Let's peek at what the agent is sending. Here's a simplified example of the JSON messages:
=== "v0.8 (Stable)"
**Defining the UI:**
```json
{"surfaceUpdate": {"surfaceId": "main", "components": [
{"id": "header", "component": {"Text": {"text": {"literalString": "Book Your Table"}, "usageHint": "h1"}}},
{"id": "date-picker", "component": {"DateTimeInput": {"label": {"literalString": "Select Date"}, "value": {"path": "/reservation/date"}, "enableDate": true}}},
{"id": "submit-text", "component": {"Text": {"text": {"literalString": "Confirm Reservation"}}}},
{"id": "submit-btn", "component": {"Button": {"child": "submit-text", "action": {"name": "confirm_booking"}}}}
]}}
```
**Populating data:**
```json
{"dataModelUpdate": {"surfaceId": "main", "contents": [
{"key": "reservation", "valueMap": [
{"key": "date", "valueString": "2025-12-15"},
{"key": "time", "valueString": "19:00"},
{"key": "guests", "valueInt": 2}
]}
]}}
```
**Signaling render:**
```json
{"beginRendering": {"surfaceId": "main", "root": "header"}}
```
=== "v0.9 (Draft)"
**Creating the surface:**
```json
{"version": "v0.9", "createSurface": {"surfaceId": "main", "catalogId": "https://a2ui.org/specification/v0_9/basic_catalog.json"}}
```
**Defining the UI:**
```json
{"version": "v0.9", "updateComponents": {"surfaceId": "main", "components": [
{"id": "header", "component": "Text", "text": "# Book Your Table", "variant": "h1"},
{"id": "date-picker", "component": "DateTimeInput", "label": "Select Date", "value": {"path": "/reservation/date"}, "enableDate": true},
{"id": "submit-text", "component": "Text", "text": "Confirm Reservation"},
{"id": "submit-btn", "component": "Button", "child": "submit-text", "variant": "primary", "action": {"event": {"name": "confirm_booking"}}}
]}}
```
**Populating data:**
```json
{"version": "v0.9", "updateDataModel": {"surfaceId": "main", "path": "/reservation", "value": {"date": "2025-12-15", "time": "19:00", "guests": 2}}}
```
Note: In v0.9, `createSurface` replaces `beginRendering`, components use a flatter format, and the data model uses plain JSON values instead of typed adjacency lists.
💡 It's Just JSON
Notice how readable and structured this is? LLMs can generate this easily, and it's safe to transmit and render—no code execution required.
The repository includes several other demos:
See all available A2UI components:
npm start -- gallery
This runs a client-only demo showcasing every standard component (Card, Button, TextField, Timeline, etc.) with live examples and code samples.
Try a different agent use case:
npm run demo:contact
This demonstrates a contact lookup agent that generates search forms and result lists.
Now that you've seen A2UI in action, you're ready to:
If port 5173 is already in use, the dev server will automatically try the next available port. Check the terminal output for the actual URL.
If you see errors about missing API keys:
echo $GEMINI_API_KEYexport GEMINI_API_KEY="your_key"If you see ERR_CONNECTION_REFUSED errors when the browser opens, don't worry — this is a known race condition (#587). The web app starts faster than the Python agent backend. Just wait a few seconds and refresh the page.
The demo agents require uv to run. If you see uv: command not found:
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# Verify
uv --version
If you encounter other Python errors:
# Make sure Python 3.10+ is available
python3 --version
# Try running the agent manually
cd samples/agent/adk/restaurant_finder
uv run .
Want to see how it works? Check out:
samples/agent/adk/restaurant_finder/ — The Python A2A agentsamples/client/lit/ — The Lit web client with A2UI rendererrenderers/lit/ (Lit) and renderers/web_core/ (framework-agnostic core)Each directory has its own README with detailed documentation.
Congratulations! You've successfully run your first A2UI application. You've seen how an AI agent can generate rich, interactive UIs that render natively in a web application—all through safe, declarative JSON messages.