# A2UI Protocol: How Agents Stream Layout Data Instead of HTML to Native UI Components

> Source: <https://dev.to/mech_app_ai/a2ui-protocol-how-agents-stream-layout-data-instead-of-html-to-native-ui-components-1e0g>
> Published: 2026-11-15 10:07:50+00:00

Most agent UIs stream markdown or generate HTML strings. A2UI streams layout data that native framework adapters translate into platform components. The agent emits structured events describing buttons, cards, and forms. The client renders them using React, Flutter, SwiftUI, or Jetpack Compose without a WebView or eval().

This separation creates strict capability boundaries. The agent cannot inject arbitrary code. It composes from a fixed catalog of components defined by the application. The protocol is event-based and optimized for low-latency streaming, making it viable for real-time agent interactions across web and mobile platforms.

Generative UI typically means an LLM writes JSX or HTML, then the client renders it. This works for demos but creates three production problems:

A2UI solves this by treating UI as data. The agent outputs a schema-validated stream of layout primitives. The client maps those primitives to native components. The agent never sees platform-specific rendering logic. The client never executes agent-generated code.

This is the same separation used in server-driven UI frameworks like Litho or Epoxy, but extended to agent-generated content.

A2UI uses an event-based protocol. The agent emits a stream of JSON events. Each event describes a UI operation: adding a component, updating state, or signaling completion.

| Event Type | Purpose | Payload | 
|---|---|---|
| `component_add` | Append a component to the layout tree | Component type, props, parent ID | 
| `component_update` | Modify existing component props | Component ID, updated props | 
| `state_sync` | Synchronize application state | State key, value | 
| `stream_end` | Signal completion of the UI stream | Optional metadata | 

The agent sends these events over HTTP streaming, WebSocket, or server-sent events. The client maintains a layout tree and applies events incrementally.

JSONL format (one JSON object per line):

```
{"type": "component_add", "id": "card-1", "component": "Card", "props": {"title": "Flight Options", "variant": "elevated"}, "parent": "root"}
{"type": "component_add", "id": "button-1", "component": "Button", "props": {"label": "Book Now", "action": "submit_booking"}, "parent": "card-1"}
{"type": "stream_end"}
```

The client receives these events, validates them against the component catalog schema, and renders them using the native framework adapter.

The application defines a component catalog. This is a schema that lists available components and their props. The agent can only compose from this catalog. If the agent references an undefined component or passes invalid props, the client rejects the event.

This enforces capability boundaries at the protocol level. The agent cannot:

The catalog is versioned. Agents and clients negotiate protocol versions during handshake. This allows safe evolution of the component library without breaking existing agents.

``` js
const catalog = {
  components: {
    Card: {
      props: {
        title: { type: 'string', required: true },
        variant: { type: 'enum', values: ['flat', 'elevated'] }
      }
    },
    Button: {
      props: {
        label: { type: 'string', required: true },
        action: { type: 'string', required: true },
        disabled: { type: 'boolean', default: false }
      }
    }
  }
};
```

The agent SDK validates outgoing events against this schema before transmission. The client validates incoming events before rendering. Both sides reject invalid data.

A2UI provides native adapters for React, Angular, Lit, Flutter, SwiftUI, and Jetpack Compose. Each adapter translates the protocol's layout data into platform-specific components.

`component_add` and `component_update` events.
The adapter maintains a mapping from A2UI component names to React components. When the agent emits a `Button` component, the adapter renders the application's `<Button>` component with the provided props.

Flutter's adapter works similarly but uses a `StreamBuilder` to rebuild the widget tree on each event. The adapter maintains a `WidgetTree` model that mirrors the A2UI layout tree. Each event triggers a rebuild of the affected subtree.

TypeScript adapters use generated types from the catalog schema. This provides compile-time validation of component props. If the catalog defines `Button.label` as a string, the TypeScript adapter enforces that type when rendering.

Flutter and Swift adapters use code generation to produce strongly typed widget builders from the catalog schema.

The protocol is optimized for low-latency streaming. Agents can emit events as soon as they decide on a component, without waiting for the full layout to be computed. This enables progressive rendering.

For example, an agent booking a flight might:

Each event is independent. The client applies them incrementally. The user sees the UI build up in real time, rather than waiting for a full page load.

The protocol supports backpressure. If the client cannot keep up with the event stream, it signals the agent to slow down. This prevents memory exhaustion on mobile devices or slow networks.

The agent SDK exposes a `canSend()` method that checks the client's readiness before emitting the next event.

A2UI separates layout state from application state. Layout state is the component tree. Application state is the data the agent and client share (user selections, form inputs, API responses).

The `state_sync` event synchronizes application state. When the user selects a flight, the client sends a `state_sync` event to the agent. The agent updates its internal state and may emit new layout events in response.

This separation allows the agent to reason about state changes without re-rendering the entire UI. Only the affected components update.

A2UI integrates with LangGraph for multi-step agent workflows. A LangGraph agent can emit A2UI events at each workflow step, building up the UI as the workflow progresses.

For example, a customer support agent might:

Each step emits A2UI events. The client renders them incrementally. The user sees a cohesive flow without page transitions.

MCP (Model Context Protocol) integration allows A2UI agents to call external tools and render the results. An agent might call a weather API via MCP, then emit a `WeatherCard` component with the response data.

The protocol is self-describing. Each event includes metadata: timestamp, agent ID, session ID. Clients can log the event stream for replay and debugging.

The A2UI Composer tool ([https://a2ui-project.github.io/composer/](https://a2ui-project.github.io/composer/)) allows developers to:

This is critical for debugging agent behavior. Instead of inspecting generated code, you inspect the event stream. You can replay a session, modify events, and see how the UI changes.

| Failure | Cause | Mitigation | 
|---|---|---|
| Invalid component reference | Agent emits unknown component name | Client rejects event, logs error, agent retries with fallback | 
| Schema validation failure | Agent sends props that violate catalog schema | Client rejects event, agent receives error response, adjusts output | 
| Stream interruption | Network failure or agent crash | Client displays last known good state, retries connection | 
| Backpressure overflow | Agent emits events faster than client can render | Client buffers events, signals backpressure, agent slows down | 
| Version mismatch | Agent uses newer catalog than client supports | Handshake fails, client requests compatible version or rejects connection | 

The protocol is designed for graceful degradation. If the agent emits an invalid event, the client rejects it but continues processing subsequent events. The UI remains functional even if parts of the layout fail to render.

A2UI agents typically run server-side. The client connects via HTTP streaming or WebSocket. This allows:

For offline scenarios, the agent can run on-device using a lightweight model. The catalog is bundled with the app. The protocol remains the same, but the transport is in-process rather than over the network.

Each client session maintains a persistent connection to the agent. This requires connection pooling and load balancing. WebSocket connections are stateful, so sticky sessions or session affinity is required.

For high-scale deployments, consider:

Use A2UI when you need strict capability boundaries for agent-generated UIs, or when the same agent must target multiple platforms (web, iOS, Android). The protocol's separation of layout data from rendering logic makes it safer than code generation and more portable than framework-specific solutions.

Avoid A2UI if your UI is static or if you already have a working server-driven UI framework. The protocol adds complexity: you must define and version a component catalog, implement framework adapters, and manage event streaming. For simple chat interfaces or markdown-based agents, the overhead is not justified.

A2UI shines when agents compose complex, multi-step UIs that must render natively across platforms. The 17,000+ stars and production adapters for six frameworks suggest real adoption beyond experimentation.
