Dynamic Schema A2UI
LLM-generated A2UI — a secondary LLM creates both the schema and data from any prompt.
This feature (declarative-gen-ui) hasn't been tagged in any LangGraph (FastAPI) cell yet. Try LangGraph (Python) instead, or browse the framework-agnostic version.
In the dynamic-schema approach, a secondary LLM generates the entire UI — schema, data, and layout — based on the conversation context. It's the most flexible A2UI flavor: the agent can render any UI for any request without pre-defined schemas.
How it works
- The primary LLM decides to call
render_a2ui(the tool the runtime auto-injects wheninjectA2UITool: true). - The runtime serializes your client-side catalog (component names +
Zod prop schemas) into the agent's
copilotkit.contextso the LLM knows which components it may emit. - The tool call streams through LangGraph as
TOOL_CALL_ARGSevents. - The A2UI middleware intercepts the stream and renders cards progressively as data items arrive.
The 3-file split
The canonical Bring-Your-Own-Catalog (BYOC) layout keeps three files
side-by-side under frontend/src/app/a2ui/:
| File | What lives there |
|---|---|
definitions.ts | Zod props schema + human-readable descriptions for each custom component — platform-agnostic so the runtime can serialise it to the LLM. |
renderers.tsx | React implementations keyed by the same names. TypeScript enforces that every definition has a renderer. |
catalog.ts | createCatalog(definitions, renderers, { includeBasicCatalog: true }) — merges your custom components with CopilotKit's built-in primitives. |
Declare your custom component definitions
Each entry pairs a Zod prop schema with a description. The description
is crucial — the LLM reads it to decide which component to emit. The
showcase's declarative-gen-ui cell ships a small dashboard catalog
(Card / StatusBadge / Metric / InfoRow / PrimaryButton):
langgraph-fastapi::declarative-gen-ui. Known demos are bundled from manifest demos[i]; check the cell id and framework slug.Implement the React renderers
Every key in myDefinitions must have a matching renderer. Props are
statically typed against the Zod schema, so refactors stay safe:
langgraph-fastapi::declarative-gen-ui. Known demos are bundled from manifest demos[i]; check the cell id and framework slug.Wire definitions × renderers into a catalog
createCatalog is what you hand to the provider. Flip
includeBasicCatalog: true to merge CopilotKit's built-ins
(Column, Row, Text, Image, Card, Button, List, Tabs, …), so the LLM
can compose custom + basic components interchangeably:
langgraph-fastapi::declarative-gen-ui. Known demos are bundled from manifest demos[i]; check the cell id and framework slug.Pass the catalog to the provider
A single prop (a2ui={{ catalog }}) is all the frontend needs — the
provider registers the catalog and wires up the built-in A2UI
activity-message renderer:
langgraph-fastapi::declarative-gen-ui. Known demos are bundled from manifest demos[i]; check the cell id and framework slug.Inject the render tool on the runtime
On the TypeScript runtime, injectA2UITool: true tells CopilotKit to
add the render_a2ui tool to the agent's tool list at request time
and serialise your client catalog into the agent's
copilotkit.context. No backend code to write — the agent can be an
empty create_agent(tools=[]):
langgraph-fastapi::declarative-gen-ui. Known demos are bundled from manifest demos[i]; check the cell id and framework slug.Progressive streaming
The secondary LLM's render_a2ui tool call streams through LangGraph
as TOOL_CALL_ARGS events. The A2UI middleware:
- Waits for the full
componentsarray before emitting anything — the schema must be complete before rendering starts. - Extracts
surfaceId+rootfrom the partial JSON. - Emits
surfaceUpdate+beginRenderingonce the schema is complete. - Extracts complete
itemsobjects progressively and emits adataModelUpdatefor each — cards appear one by one as data streams in.
A built-in progress indicator shows while the schema is still generating and hides automatically once data items start arriving.
When should I use dynamic schemas?
- You don't know the UI shape ahead of time — the agent decides what to show based on the user's request.
- You want to prototype A2UI without committing to a schema file yet.
- You're building a conversational dashboard where the layout varies per turn.
If the surface is well-known (e.g. a product card, a flight result), prefer a fixed schema — it's faster, cheaper, and the UI is deterministic.