Skip to main content

A2UI — Agent to UI

A2UI lets an Expert describe an interface as declarative JSON, which the frontend renders as native React components inline in the conversation — no iframe, no HTML strings, so it matches the product’s own design system exactly. It ships with the profy-visualize plugin under the tool name render_ui. It runs in parallel with visualize (free-form HTML / Canvas / WebGL): use A2UI for “data + layout”, use visualize for custom rendering logic.
Current boundary: A2UI is a display surface, not an interaction loop. A button click dispatches a profy:a2ui-action window event, but nothing in the codebase listens for it — the click never reaches the Expert and never triggers another turn.So: using A2UI to present (comparison tables, status panels, result summaries) is reliable; using it as a “click to continue” confirmation mechanism is not. When you need confirmation, have the Expert ask in text or use the human-in-the-loop path.

Wire protocol

render_ui takes a component array and returns an a2ui payload; the frontend StageGroup detects that field and renders it through A2uiSurfaceCard.

The shape of a component node

This is the easiest thing to get wrong — properties must live under props, not flattened onto the node:
A node has three optional fields: props (property map), children (child components), actions (action definitions).

The only input validation

render_ui validates exactly one thing: every component, recursively, has a type field. Missing it rejects the whole call:
Beyond that, property names and types are not validated. A wrong property name raises no error — it silently renders as blank or as the default. That is the single most useful fact when debugging an A2UI surface.

Component catalog (16)

Property names below are the fields the renderer actually reads.

Layout

gap only recognises 0 / 1 / 2 / 3 / 4 / 6 / 8 (Tailwind spacing steps); any other number falls back to 3. So gap: 16 does not produce 16px — it produces the default. align recognises center, end (or bottom) and stretch; anything else falls back to start.

Content

Text’s weight recognises bold, semibold and medium; anything else stays normal. Note there is no code variant — passing it renders body text.

Interactive

Variants recognised: secondary, destructive, outline. Both primary and ghost fall through to the default style (visually equivalent to primary). Button size is fixed at sm.

Profy business components

PricingTable cannot highlight a recommended tier, and it will not expand a features array into a bullet list — arrays are String()d into a,b,c on one line. For a good-looking feature comparison, hand-building a Table gives you more control.

What happens with an unknown component

When the renderer has no entry for a type, it renders a dashed box reading Unknown component: <type> and still renders that node’s children — it never crashes the surface. So a dashed box means a typo or a component that doesn’t exist (for example ProgressBar, which is not in the catalog).

Worked examples

Account status panel

Data table

Note the shape of headers and rows — this is where people trip:

List

Key numbers

Failures and fixes

The component guide shown to the model has known drift from the renderer. Several property names in the A2UI prompt (Badge’s color, Table’s columns, PlanBadge’s plan, CreditDisplay’s unit) don’t match the fields the renderer reads, and its examples flatten props. Because render_ui only validates type, this drift raises no error — it silently renders as blank or default.So when an A2UI panel looks under-filled, check the property names on this page first rather than suspecting the underlying data.

Best practices

  1. Always nest properties under props — this is the number-one source of silent failure.
  2. Use Card as the top-level container so related components form one visual unit.
  3. Keep nesting under three levels; deep layouts are fragile and hard to read.
  4. Present with it, don’t interact through it — ask in text when a decision is needed.
  5. Go to visualize for charts, 3D and animation; A2UI has only these 16 predefined components.
  6. Follow a surface with a sentence of summary — A2UI is additive, not a replacement for explanation.
Verified 2026-08-12. Sources: apps/web/src/lib/a2ui/{definitions.ts,renderers.tsx}, apps/web/src/components/agent/chat-area/card/A2uiSurfaceCard.tsx, packages/types/src/a2ui.ts, services/agent-runtime/src/plugins/builtin/visualize/tools/render_ui.py.

Visualize plugin

Free-form HTML / Canvas / WebGL surfaces

Plugin catalog

The full inventory of 28 built-in plugins