TL;DR — A2UI (Agent-to-UI) is Google's open-source protocol that lets AI agents generate rich, interactive user interfaces by emitting declarative JSON — never executable code. The client renders these descriptions using its own native component library (React, Flutter, Lit), enforcing a strict data-vs-code security boundary. Version 0.9, released in early 2026, introduces a prompt-first architecture, an official React renderer, and a Python Agent SDK, making A2UI the emerging standard for agent-driven UIs.

Key Takeaways

  • Declarative, not executable — A2UI agents describe UI intent as JSON. Clients render it with trusted, pre-approved components. No eval(), no injected scripts, no server-rendered HTML blobs.
  • One protocol, many platforms — Official renderers exist for React, Lit, Angular, and Flutter. SwiftUI and Jetpack Compose are on the roadmap for Q2 2026.
  • Security by architecture — The data-vs-code separation is not a policy; it is a structural guarantee. Agents cannot escape the component catalog or execute arbitrary logic on the client.
  • Completes the protocol stack — A2UI handles agent → user interfaces, A2A handles agent → agent communication, and MCP handles agent → tool connections. Together they cover the entire interaction surface.
  • v0.9 is prompt-first — The schema lives in the model's system prompt, unlocking richer layouts and removing the constraints of structured-output-only generation.

The "Chat Wall" Problem

For two years, the dominant interface for AI agents has been a chat thread: a vertically scrolling list of text bubbles. This works for conversational Q&A. It fails spectacularly the moment the agent needs to present structured information.

Consider a travel-booking agent. The user asks: "Show me flights from SFO to NRT next Thursday."

Without A2UI (text-only chat):

code
Agent: Here are the available flights:

1. JAL 002 — Departs 11:30 AM, Arrives 3:30 PM+1 — $1,240 Economy
2. ANA 008 — Departs 5:15 PM, Arrives 9:20 PM+1 — $1,180 Economy
3. United 837 — Departs 1:45 PM, Arrives 5:50 PM+1 — $980 Economy

Would you like to book one? Reply with the number.

The user must parse a wall of text, mentally compare three options, and type a number. There is no way to filter, sort, or visually compare side-by-side.

With A2UI (native UI):

The agent emits a JSON description specifying a FlightCard component for each option, a SortDropdown, and a BookButton with a bound action. The client renders native cards with airline logos, price highlights, and a single-tap booking flow — all without the agent shipping a single line of HTML or JavaScript.

sequenceDiagram participant User participant Client as "Client (React App)" participant Agent as "AI Agent" User->>Agent: "Flights SFO→NRT Thu" Agent->>Client: createSurface (JSON) Agent->>Client: updateComponents (FlightCard × 3) Client->>User: Native flight cards rendered User->>Client: Taps "Book" on ANA 008 Client->>Agent: action event (book, flightId=ANA008)

This is the promise of A2UI: agents stop fighting the chat wall and start generating real interfaces.

What Is A2UI?

A2UI (Agent-to-UI) is an open-source protocol released by Google under the Apache 2.0 license. It defines a declarative JSON format that AI agents use to describe user interfaces. The agent never generates rendering code — it generates a description of what the UI should look like and how it should behave. The client interprets this description and renders it with its own trusted component library.

The protocol has two spec versions:

Version Status Architecture Best For
v0.8 Stable Structured output (constrained decoding) Models with native JSON mode
v0.9 Draft Prompt-first (schema in system prompt) Richer layouts, flexible models

A2UI sits in a specific layer of the agent stack. It does not replace MCP (which connects agents to tools and data sources) or A2A (which enables multi-agent coordination). It exclusively handles the agent-to-user interface layer — the last mile where agent output becomes a visual, interactive experience.

Think of it this way: MCP is how an agent reads a database. A2A is how two agents coordinate a handoff. A2UI is how the agent shows the results to a human.

A2UI Architecture: How It Works

The Message Flow

An A2UI session is a stream of JSONL messages between the agent and the client. The protocol defines four core message types:

Message Direction Purpose
createSurface Agent → Client Opens a new UI surface (panel, modal, inline card)
updateComponents Agent → Client Adds or replaces components on a surface
updateDataModel Agent → Client Updates the data that components are bound to
deleteSurface Agent → Client Closes and cleans up a surface
graph LR Start(("Start")) -->|"createSurface"| SC["Surface Created"] SC -->|"updateComponents"| CR["Components Rendered"] CR -->|"updateDataModel"| DU["Data Updated"] DU -->|"updateComponents"| CR CR -->|"deleteSurface"| End(("End")) DU -->|"deleteSurface"| End

The Component Tree: Adjacency List Model

A2UI does not use nested JSON to describe component hierarchy. Instead, it uses a flat adjacency list — every component is a top-level entry with an explicit parentId pointer. This design was chosen for two reasons: (1) LLMs generate flat arrays more reliably than deeply nested structures, and (2) it enables incremental updates — the agent can add a single component without re-sending the entire tree.

json
{
  "type": "updateComponents",
  "surfaceId": "flight-results",
  "components": [
    {
      "componentId": "root",
      "type": "Container",
      "parentId": null,
      "properties": { "direction": "column", "gap": "16px" }
    },
    {
      "componentId": "header",
      "type": "Text",
      "parentId": "root",
      "properties": { "content": "Flights: SFO → NRT", "variant": "h2" }
    },
    {
      "componentId": "sort",
      "type": "Dropdown",
      "parentId": "root",
      "properties": {
        "label": "Sort by",
        "options": ["Price", "Duration", "Departure"],
        "dataRef": "/ui/sortField"
      }
    },
    {
      "componentId": "flight-list",
      "type": "List",
      "parentId": "root",
      "properties": { "dataRef": "/flights", "itemTemplate": "flight-card-tmpl" }
    }
  ]
}

If you work with JSON daily, our JSON Formatter can help you pretty-print and inspect A2UI payloads like the one above.

Data Binding with JSON Pointers (RFC 6901)

Components do not embed their data directly. Instead, they reference paths in a separate data model using JSON Pointers (RFC 6901). The dataRef property /flights in the example above points to an array maintained by the agent via updateDataModel messages.

json
{
  "type": "updateDataModel",
  "surfaceId": "flight-results",
  "operations": [
    {
      "op": "replace",
      "path": "/flights",
      "value": [
        { "id": "JAL002", "airline": "JAL", "price": 1240, "depart": "11:30" },
        { "id": "ANA008", "airline": "ANA", "price": 1180, "depart": "17:15" },
        { "id": "UA837", "airline": "United", "price": 980, "depart": "13:45" }
      ]
    },
    {
      "op": "replace",
      "path": "/ui/sortField",
      "value": "Price"
    }
  ]
}

This separation is critical. The component tree defines structure and behavior. The data model defines content. The agent can update prices (data model) without re-sending the UI structure, and can rearrange the layout (components) without re-sending the data.

The Security Model: Data vs Code

The core security philosophy of A2UI can be stated in one sentence: agents send data, clients execute code. This is not a guideline — it is an architectural constraint enforced at the protocol level.

How A2UI Prevents Code Injection

In traditional approaches, an AI agent might generate a React component, an HTML snippet, or a JavaScript callback. Any of these could contain malicious code — whether from a prompt injection attack or a hallucination. A2UI eliminates this entire attack surface:

Aspect Traditional (Embedded Code) A2UI (Declarative JSON)
Agent output HTML/JS/React code JSON description
Code execution Agent-generated code runs on client Only pre-approved client code runs
Component set Unlimited (any HTML tag) Trusted catalog only
Event handlers Arbitrary onClick functions Named functions from registry
Data access Full DOM/API access Scoped data model only
Prompt injection risk High — injected code executes Minimal — injected JSON is just data

The Trusted Component Catalog

The client maintains a catalog of components the agent is allowed to request. If the agent emits a component type that is not in the catalog, the client ignores it or renders a fallback. The agent cannot invent new component types at runtime.

javascript
// Client-side component catalog (React example)
const COMPONENT_CATALOG = {
  'Container': ContainerComponent,
  'Text': TextComponent,
  'Button': ButtonComponent,
  'Dropdown': DropdownComponent,
  'List': ListComponent,
  'FlightCard': FlightCardComponent,
  'Image': ImageComponent,
};

function renderA2UIComponent(descriptor) {
  const Component = COMPONENT_CATALOG[descriptor.type];
  if (!Component) {
    console.warn(`Unknown component type: ${descriptor.type}`);
    return <FallbackComponent />;
  }
  return <Component {...descriptor.properties} />;
}

The Function Registry

Even client-side interactivity (button clicks, form submissions) uses named functions from a pre-registered catalog — not arbitrary code:

json
{
  "componentId": "book-btn",
  "type": "Button",
  "parentId": "flight-list",
  "properties": {
    "label": "Book Now",
    "action": {
      "functionName": "bookFlight",
      "args": { "flightId": { "$dataRef": "/selectedFlight/id" } }
    }
  }
}

The bookFlight function is implemented and audited by the client developer. The agent can invoke it and pass data-model references as arguments, but it cannot define what the function does. This is how A2UI achieves tool use safety at the UI layer.

A2UI v0.9: What Changed

Version 0.8 was designed for models that support structured output (constrained decoding). The JSON schema was enforced at the decoding layer, which guaranteed valid output but limited expressiveness — the schema had to stay small enough for the model to track.

Version 0.9 shifts to a prompt-first architecture. The full JSON schema is embedded in the model's system prompt, and the model generates free-form JSON that is validated post-hoc. This unlocks several improvements:

Feature v0.8 v0.9
Schema delivery Constrained decoding config System prompt embedding
Schema complexity Limited by decoder constraints Arbitrarily rich
Web renderers Lit only Lit + React + Angular (shared web-core)
Agent SDK None (raw JSON) Python SDK (a2ui-agent)
Schema files Single monolithic spec Modular per-component schemas
Streaming JSONL JSONL (unchanged)

v0.8 vs v0.9 JSON Comparison

v0.8 — Flat, minimal properties:

json
{
  "component": "text-input",
  "id": "email",
  "label": "Email",
  "validation": "email"
}

v0.9 — Richer properties, explicit surface binding, data refs:

json
{
  "componentId": "email-input",
  "type": "TextInput",
  "parentId": "form-root",
  "surfaceId": "contact-form",
  "properties": {
    "label": "Email Address",
    "placeholder": "you@example.com",
    "dataRef": "/form/email",
    "validation": {
      "pattern": "^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$",
      "errorMessage": "Please enter a valid email"
    }
  }
}

New in v0.9: Shared web-core Library

The v0.9 spec introduces @anthropic-ai/a2ui-web-core (contributed by Google to a shared repo), a platform-agnostic TypeScript library that handles:

  • JSON Pointer resolution and data model management
  • Component tree reconciliation (diff and patch)
  • Validation of incoming agent messages against modular schemas
  • Event routing from UI actions back to the agent

Framework-specific renderers (React, Lit, Angular) are thin wrappers around web-core, reducing duplication and ensuring behavioral consistency.

Renderers: One Protocol, Many Platforms

A2UI's platform-agnostic design means any rendering technology can implement a renderer. Here is the current landscape:

Renderer Platform Status Package
React Web ✅ Official (v0.9) @a2ui/react-renderer
Lit Web ✅ Official (v0.8+) @a2ui/lit-renderer
Angular Web ✅ Community a2ui-angular
Flutter Mobile + Desktop ✅ Official a2ui_flutter
SwiftUI iOS / macOS 🔜 Planned Q2 2026
Jetpack Compose Android 🔜 Planned Q2 2026

The React renderer is the most feature-complete for web applications. It maps A2UI component types to React components, manages the data model as React state, and routes user actions back to the agent as events.

graph TD A["AI Agent"] -->|"JSONL stream"| B["web-core"] B -->|"Component tree"| C{"Renderer"} C -->|React| D["React App"] C -->|Lit| E["Lit App"] C -->|Flutter| F["Flutter App"] D -->|"User events"| A E -->|"User events"| A F -->|"User events"| A

Building with A2UI: Practical Example

Let us build a complete contact form — the "Hello World" of A2UI. The agent creates a surface, adds form components, binds them to a data model, and handles the submission.

Step 1: Agent Emits JSONL Stream

jsonl
{"type":"createSurface","surfaceId":"contact-form","title":"Contact Us","mode":"inline"}
{"type":"updateComponents","surfaceId":"contact-form","components":[{"componentId":"form-root","type":"Container","parentId":null,"properties":{"direction":"column","gap":"12px"}},{"componentId":"name-input","type":"TextInput","parentId":"form-root","properties":{"label":"Full Name","dataRef":"/form/name","required":true}},{"componentId":"email-input","type":"TextInput","parentId":"form-root","properties":{"label":"Email","dataRef":"/form/email","validation":{"pattern":"^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$","errorMessage":"Invalid email"}}},{"componentId":"msg-input","type":"TextArea","parentId":"form-root","properties":{"label":"Message","dataRef":"/form/message","rows":4}},{"componentId":"submit-btn","type":"Button","parentId":"form-root","properties":{"label":"Send Message","variant":"primary","action":{"functionName":"submitContact","args":{"data":{"$dataRef":"/form"}}}}}]}
{"type":"updateDataModel","surfaceId":"contact-form","operations":[{"op":"replace","path":"/form","value":{"name":"","email":"","message":""}}]}

You can validate this JSONL by pasting each line into our JSON Validator to confirm each message is well-formed.

Step 2: Python Agent SDK

The v0.9 Python SDK (a2ui-agent) provides high-level helpers so you do not have to hand-craft JSONL:

python
from a2ui_agent import A2UIAgent, Surface, components as c

agent = A2UIAgent(model="gemini-2.5-pro")

# Define the UI
surface = Surface(id="contact-form", title="Contact Us", mode="inline")

surface.add(c.Container(id="form-root", direction="column", gap="12px"))
surface.add(c.TextInput(
    id="name-input",
    parent="form-root",
    label="Full Name",
    data_ref="/form/name",
    required=True,
))
surface.add(c.TextInput(
    id="email-input",
    parent="form-root",
    label="Email",
    data_ref="/form/email",
    validation={"pattern": r"^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,}$"},
))
surface.add(c.TextArea(
    id="msg-input",
    parent="form-root",
    label="Message",
    data_ref="/form/message",
    rows=4,
))
surface.add(c.Button(
    id="submit-btn",
    parent="form-root",
    label="Send Message",
    variant="primary",
    action={"functionName": "submitContact", "args": {"data": {"$dataRef": "/form"}}},
))

# Initialize data model
surface.set_data("/form", {"name": "", "email": "", "message": ""})

# Emit to client
async for message in agent.stream(surface):
    await websocket.send(message)

Step 3: React Client Rendering

jsx
import { A2UIProvider, A2UISurface } from '@a2ui/react-renderer';

const FUNCTION_REGISTRY = {
  submitContact: async ({ data }) => {
    const response = await fetch('/api/contact', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    });
    return response.ok;
  },
};

function App() {
  return (
    <A2UIProvider
      agentUrl="wss://agent.example.com/a2ui"
      functionRegistry={FUNCTION_REGISTRY}
    >
      <A2UISurface surfaceId="contact-form" />
    </A2UIProvider>
  );
}

The client developer controls what submitContact does. The agent controls what the form looks like and how data flows. Neither can encroach on the other's domain.

A2UI in the Agent Protocol Stack

Modern agent systems do not run on a single protocol. They rely on a stack where each layer handles a different kind of interaction. Here is how A2UI fits alongside MCP and A2A:

graph TB subgraph "User Layer" U["Human User"] end subgraph "A2UI Layer" A2UI["A2UI Protocol"] end subgraph "Agent Layer" AG1["Agent A"] AG2["Agent B"] end subgraph "A2A Layer" A2A["A2A Protocol"] end subgraph "Tool Layer" MCP["MCP Protocol"] T1["Database"] T2["API"] T3["File System"] end U <-->|"Declarative UI"| A2UI A2UI <-->|"JSON surfaces"| AG1 AG1 <-->|"Agent-to-Agent"| A2A A2A <-->|"Task delegation"| AG2 AG1 <-->|"Tool calls"| MCP AG2 <-->|"Tool calls"| MCP MCP --> T1 MCP --> T2 MCP --> T3
Protocol Layer Direction Payload
A2UI Agent ↔ User Bidirectional (UI + events) Declarative JSON surfaces
A2A Agent ↔ Agent Bidirectional (tasks + results) JSON-RPC task messages
MCP Agent ↔ Tool Bidirectional (calls + responses) JSON-RPC tool calls

In a real-world agentic workflow, a user might request a complex operation via an A2UI surface. The primary agent delegates subtasks to specialized agents via A2A. Each agent calls tools via MCP. Results flow back up the stack and are rendered as updated A2UI surfaces. For a deeper look at agent-to-agent patterns, see our Multi-Agent System Complete Guide.

Best Practices

1. Keep surfaces small and focused. A surface should represent a single task or view — a form, a results panel, a confirmation dialog. Do not try to build a full SPA inside one surface. If the user's task changes, create a new surface.

2. Prefer data model updates over component re-sends. When only the content changes (new search results, updated prices), use updateDataModel. Only send updateComponents when the structure of the UI changes. This minimizes payload size and makes the protocol efficient for streaming.

3. Version your component catalog. Your trusted component catalog will evolve. Use semantic versioning and include the catalog version in the createSurface message so the agent knows what components are available on the client.

4. Validate agent output on the client. Even though A2UI is declarative, always validate incoming JSON against the schema before rendering. The web-core library does this automatically, but if you build a custom renderer, use JSON Schema validation at the ingestion layer.

5. Design for progressive enhancement. Not all clients will support all component types. Design your agent to include fallback properties — for example, a FlightCard might have a fallbackText that renders in text-only clients.

FAQ

What is A2UI protocol?

A2UI (Agent to UI) is an open-source declarative UI protocol created by Google and released under the Apache 2.0 license. It enables AI agents to generate rich, interactive user interfaces by emitting JSON descriptions rather than executable code. The client application — whether built with React, Flutter, Angular, or Lit — interprets these descriptions and renders them using its own native component library.

The key innovation is the strict separation between data (what the agent sends) and code (what the client executes). The agent can describe a button with a click handler, but it cannot define what that click handler does. This architectural constraint eliminates an entire class of security vulnerabilities, including prompt injection attacks that attempt to execute malicious code through the UI layer.

How does A2UI ensure security?

A2UI's security model is structural, not procedural. Three mechanisms work together. First, the agent output is always declarative JSON — there is no syntax in the protocol for embedding executable code. Second, the client maintains a trusted component catalog: a whitelist of component types the agent is allowed to request. Any unknown component type is rejected or rendered as a safe fallback. Third, client-side interactivity (button clicks, form submissions, navigation) uses a function registry of named, pre-audited functions. The agent references functions by name and passes data, but cannot define or modify function behavior.

This design means that even a perfectly crafted prompt injection attack — one that convinces the agent to emit malicious instructions — is limited to requesting components and functions that the client developer has already approved.

What is the difference between A2UI v0.8 and v0.9?

Version 0.8 was designed for LLMs with constrained decoding capabilities (structured output). The JSON schema was enforced at the decoder level, guaranteeing that every token the model generated was valid according to the schema. The downside was that schemas had to be kept small — complex layouts could exceed the decoder's tracking capacity.

Version 0.9 takes a prompt-first approach. The full JSON schema is embedded in the model's system prompt, and the model generates JSON freely. Validation happens post-generation on the client. This enables much richer component schemas with nested properties, conditional validation, and complex data binding. V0.9 also ships a shared web-core TypeScript library, an official React renderer, and a Python Agent SDK (a2ui-agent) that simplifies server-side surface construction.

Which platforms does A2UI support?

A2UI is platform-agnostic by design — any rendering technology can implement a renderer. As of April 2026, official renderers exist for React (web), Lit (web), and Flutter (mobile + desktop). A community-maintained Angular renderer is also available. Google has announced plans for SwiftUI (iOS/macOS) and Jetpack Compose (Android) renderers in Q2 2026.

The shared web-core library handles the protocol-level logic (message parsing, data model management, component tree reconciliation), so building a new renderer primarily involves mapping A2UI component types to the target platform's native components.

How does A2UI relate to A2A and MCP protocols?

The three protocols form a complete interaction stack for modern agent systems. MCP (Model Context Protocol) handles the agent-to-tool layer — it defines how agents call databases, APIs, file systems, and other external resources. A2A (Agent-to-Agent) handles the agent-to-agent layer — it enables multi-agent coordination, task delegation, and result aggregation. A2UI handles the agent-to-user layer — it defines how agents present information and collect input through visual interfaces.

In a production agentic workflow, all three protocols typically operate simultaneously. A user interacts with an A2UI surface, which triggers the primary agent. That agent may delegate to other agents via A2A and call tools via MCP. Results flow back through the stack and are rendered as updated A2UI surfaces. For framework comparisons, see our AI Agent Framework Comparison 2026.

Summary

A2UI addresses a fundamental gap in the agent ecosystem: the inability of AI agents to generate safe, rich, cross-platform user interfaces. By enforcing declarative JSON as the sole communication format and maintaining a strict data-vs-code boundary, the protocol makes it possible for agents to create interactive experiences without the security nightmares of code generation.

Version 0.9 marks a maturity inflection point. The prompt-first architecture, shared web-core library, and official React renderer make A2UI production-ready for web applications today. With Flutter covering mobile and desktop, and SwiftUI/Compose renderers on the horizon, the protocol is positioned to become the standard agent-to-user interface layer alongside MCP and A2A.

If you are building agent-powered products, the time to evaluate A2UI is now. Start with the official React renderer, define your trusted component catalog, and experience the difference between an agent that chats and an agent that ships interfaces.

For deeper context on the broader agent ecosystem, explore our AI Agent Development Complete Guide and the MCP Protocol Complete Guide.