From copilotkit
Render A2UI (Agent-to-UI) declarative surfaces in CopilotKit v2. Covers runtime setup, client provider, and low-level primitives for theme customization and surface management.
How this skill is triggered — by the user, by Claude, or both
Slash command
/copilotkit:a2ui-rendererThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill builds on copilotkit/react-core (for `CopilotKit` provider fundamentals) and
This skill builds on copilotkit/react-core (for CopilotKit provider fundamentals) and
copilotkit/runtime (for CopilotRuntime fundamentals). Read those first.
A2UI has two halves. The runtime declares a2ui middleware; the client enables
the a2ui prop on the provider. Once both are set, /info flags A2UI and the
client auto-mounts createA2UIMessageRenderer — you do NOT wire
renderActivityMessages yourself.
app/routes/api.copilotkit.$.tsx)import type { Route } from "./+types/api.copilotkit.$";
import {
CopilotRuntime,
createCopilotRuntimeHandler,
BuiltInAgent,
convertInputToTanStackAI,
} from "@copilotkit/runtime/v2";
import { chat } from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";
const agent = new BuiltInAgent({
type: "tanstack",
factory: ({ input, abortController }) => {
const { messages, systemPrompts } = convertInputToTanStackAI(input);
return chat({
adapter: openaiText("gpt-4o"),
messages,
systemPrompts,
abortController,
});
},
});
const runtime = new CopilotRuntime({
agents: { default: agent },
// Enabling this key causes /info to advertise A2UI to the client.
a2ui: {},
});
const handler = createCopilotRuntimeHandler({
runtime,
basePath: "/api/copilotkit",
});
export async function loader({ request }: Route.LoaderArgs) {
return handler(request);
}
export async function action({ request }: Route.ActionArgs) {
return handler(request);
}
app/root.tsx or the app shell)import { CopilotKit, CopilotChat } from "@copilotkit/react-core/v2";
import "@copilotkit/react-core/v2/styles.css";
export default function App() {
return (
<CopilotKit
runtimeUrl="/api/copilotkit"
a2ui={{
theme: {
// Theme object forwarded to A2UIProvider → ThemeProvider.
// Tokens map to A2UI's basic catalog CSS vars.
colors: { primary: "#0ea5e9" },
},
}}
>
<CopilotChat agentId="default" className="h-full" />
</CopilotKit>
);
}
Pass a custom catalog to extend the built-in component set. createCatalog
and extractSchema let the agent see what components it may render.
import { createCatalog } from "@copilotkit/a2ui-renderer";
import { z } from "zod";
const theme = { colors: { primary: "#0ea5e9" } };
// Definitions are platform-agnostic (Zod schemas + descriptions).
// Renderers are platform-specific (React components).
// TypeScript enforces that renderer keys match definition keys exactly.
const definitions = {
ProductCard: {
description: "A product card with title and price",
props: z.object({ title: z.string(), price: z.number() }),
},
};
const catalog = createCatalog(
definitions,
{
ProductCard: ({ props }) => (
<div className="rounded-xl border p-3">
<div className="font-medium">{props.title}</div>
<div className="text-sm text-muted-foreground">${props.price}</div>
</div>
),
},
{ includeBasicCatalog: true },
);
<CopilotKit runtimeUrl="/api/copilotkit" a2ui={{ theme, catalog }}>
<CopilotChat agentId="default" />
</CopilotKit>;
extractSchema(definitions) is available for passing a JSON-serializable
view of the definitions to the runtime's a2ui.schema config — it is not
a generic type helper. Type parameters erase at runtime; the agent needs a
real runtime schema value (Zod).
<CopilotKit
runtimeUrl="/api/copilotkit"
a2ui={{
theme,
loadingComponent: () => <div className="animate-pulse">Building UI…</div>,
}}
>
<CopilotChat agentId="default" />
</CopilotKit>
Wrong:
// server
new CopilotRuntime({ agents: { default: agent } });
// client
<CopilotKit runtimeUrl="/api/copilotkit" a2ui={{ theme }} />;
Correct:
// server
new CopilotRuntime({ agents: { default: agent }, a2ui: {} });
// client
<CopilotKit runtimeUrl="/api/copilotkit" a2ui={{ theme }} />;
Without runtime.a2ui, /info never flags A2UI and the provider's a2ui prop
silently no-ops — the renderer never mounts.
Source: packages/runtime/src/v2/runtime/core/runtime.ts:55-58,217,242
Wrong:
import { createA2UIMessageRenderer } from "@copilotkit/react-core/v2";
<CopilotKit
runtimeUrl="/api/copilotkit"
renderActivityMessages={[createA2UIMessageRenderer({ theme })]}
/>;
Correct:
<CopilotKit runtimeUrl="/api/copilotkit" a2ui={{ theme }} />
The CopilotKit provider auto-detects runtime A2UI via /info and injects the
built-in renderer. Passing it through renderActivityMessages duplicates the
renderer and can race with the auto-injected one.
Source: packages/react-core/src/v2/providers/CopilotKitProvider.tsx:188-222,294-296
Wrong:
# Pseudocode — inside your agent generator. Exact API names/kwargs vary by
# A2UI SDK version; consult your SDK's docs for real call shapes.
async def agent_generator():
# agent re-emits createSurface operation on every state delta
async for update in stream:
yield a2ui.create_surface(surface_id="main", ...) # every tick
yield a2ui.update_components(...)
Correct:
# Pseudocode — inside your agent generator.
# Emit createSurface once per surfaceId; use updateComponents / updateDataModel
# for changes.
async def agent_generator():
yield a2ui.create_surface(surface_id="main", ...) # once
async for update in stream:
yield a2ui.update_components(surface_id="main", ...)
The MessageProcessor dedups on surfaceId but re-emitting is an agent-side
bug — the client re-runs reconciliation logic for nothing and flickers.
Source: packages/react-core/src/v2/a2ui/A2UIMessageRenderer.tsx:218-226
Wrong:
copilotkit.setProperties({ ...copilotkit.properties, a2uiAction: msg });
await copilotkit.runAgent({ agent });
// no finally — a2uiAction leaks into the next run's properties
Correct:
try {
copilotkit.setProperties({ ...copilotkit.properties, a2uiAction: msg });
await copilotkit.runAgent({ agent });
} finally {
if (copilotkit.properties) {
const { a2uiAction, ...rest } = copilotkit.properties;
copilotkit.setProperties(rest);
}
}
The built-in bridge always strips a2uiAction in finally, guarded by a
copilotkit.properties null-check so it can't mask the original runAgent
error with a TypeError during destructuring. Skipping cleanup keeps the
previous action attached to subsequent runs.
Source: packages/react-core/src/v2/a2ui/A2UIMessageRenderer.tsx:146-167
Wrong:
import { createA2UIMessageRenderer } from "@copilotkitnext/a2ui-renderer";
Correct:
// Low-level primitives (rarely needed — the CopilotKit provider's a2ui prop is the default path):
import {
A2UIProvider,
A2UIRenderer,
createCatalog,
} from "@copilotkit/a2ui-renderer";
// Auto-mounted renderer lives in react-core/v2:
import { createA2UIMessageRenderer } from "@copilotkit/react-core/v2";
This package ships as @copilotkit/a2ui-renderer, not
@copilotkitnext/a2ui-renderer. The @copilotkitnext/ scope is reserved
for other packages that ship under it separately — do not assume it applies
here.
Source: packages/a2ui-renderer/package.json
npx claudepluginhub justinlietz93/copilotkit9plugins reuse this skill
First indexed Jun 11, 2026
Showing the 6 earliest of 9 plugins
Render A2UI (Agent-to-UI) declarative surfaces in CopilotKit v2. Covers runtime setup, client provider, and low-level primitives for theme customization and surface management.
Guides on assistant-ui React library for AI chat UI components, covering architecture, runtime adapters like AI SDK and LangGraph, packages, and debugging.
This skill should be used when the user asks about "generative UI", "dynamic UI", "AI-generated interfaces", "json-render", "render JSON as UI", "generate a dashboard", "create dynamic components", "AI UI generation", "MCP App UI", "deliver UI in chat", "interactive chat interface", or needs to decide whether to use static components vs AI-generated UI. Covers the json-render framework, renderer selection, catalog design, MCP Apps delivery (ui:// resources for in-chat interactive UIs), and integration with gemskills for visual asset generation.