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 theprofy-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.
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 underprops, not flattened onto the node:
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:
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 readingUnknown 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 ofheaders and rows — this is where people trip:
List
Key numbers
Failures and fixes
Best practices
- Always nest properties under
props— this is the number-one source of silent failure. - Use Card as the top-level container so related components form one visual unit.
- Keep nesting under three levels; deep layouts are fragile and hard to read.
- Present with it, don’t interact through it — ask in text when a decision is needed.
- Go to
visualizefor charts, 3D and animation; A2UI has only these 16 predefined components. - 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.Related
Visualize plugin
Free-form HTML / Canvas / WebGL surfaces
Plugin catalog
The full inventory of 28 built-in plugins

