Components and surfaces
Let your agent render your React components, in the chat and in areas of your page.
Open your coding agent in your project's folder, or in an empty folder for a new app.This runs in a coding agent on your computer.
Experimental
Components and surfaces implements A2UI 1.0 as of spec revision cbaa5ee (October 2026). Upstream still calls 1.0 a candidate, and the API of this feature can change. What the support matrix lists is the supported scope; anything it does not list is not supported, rather than partly supported.
What is this?#
A text answer is often the wrong answer. "Compare these three plans" wants a table. "Show me cheap running shoes" wants product cards, and maybe in the product row of your page, not in the chat.
With components and surfaces, the agent renders real React components while it answers:
- In the chat, inside its reply.
- In areas of your page, outside the chat. Asking the chat changes the page.
You decide what the agent can render. The agent decides what to show and fills in the values. Users can type into the UI, press its buttons, and ask follow-up questions about it.
Try it#
These steps add generative UI to a Next.js app, with an OpenAI model through TanStack AI.
Already have CopilotKit running? Do step 3 only. Your runtime needs no change: when the provider sends a catalog, the runtime gives that run a render_ui tool and a short guide. Your agent must pass the run's tools and context to its model, as in step 2. For a LangGraph Python agent, add CopilotKitMiddleware.
Install the packages#
npm install @copilotkit/react-core @copilotkit/runtime @tanstack/ai @tanstack/ai-openaiPut your OpenAI key in .env.local:
OPENAI_API_KEY=sk-...Add the runtime and an agent#
The agent is a built-in agent in factory mode: you call the model yourself with TanStack AI.
import { chat } from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";
import {
BuiltInAgent,
convertInputToTanStackAI,
CopilotRuntime,
createCopilotRuntimeHandler,
InMemoryAgentRunner,
} from "@copilotkit/runtime/v2";
const agent = new BuiltInAgent({
type: "tanstack",
factory: ({ input, abortController }) => {
const { messages, systemPrompts, tools } = convertInputToTanStackAI(input);
return chat({
adapter: openaiText("gpt-5.5"),
messages,
systemPrompts: [
"You are a helpful assistant. When a visual answer helps, render it with generative UI.",
...systemPrompts,
],
tools,
abortController,
});
},
});
const runtime = new CopilotRuntime({
agents: { default: agent },
runner: new InMemoryAgentRunner(),
});
const handler = createCopilotRuntimeHandler({ runtime, basePath: "/api/copilotkit" });
export const GET = handler;
export const POST = handler;Pass on both values from convertInputToTanStackAI:
toolshas therender_uitool. Without it, the model cannot render.systemPromptshas the guide: the components, their props, and how to callrender_ui. Without it, the model does not know what to render.
Add your own tools and prompts next to them. Do not replace them.
Turn on generative UI#
Give the provider the standard catalog. It has 69 ready-made components: layout, text, inputs, tables, charts, maps, and more.
"use client";
import { CopilotKitProvider } from "@copilotkit/react-core/v2";
import { standardUI } from "@copilotkit/react-core/v2/generative-ui";
import "@copilotkit/react-core/v2/styles.css";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<CopilotKitProvider runtimeUrl="/api/copilotkit" generativeUI={{ ui: standardUI }}>
{children}
</CopilotKitProvider>
);
}The catalog holds React components, so the provider must be created in a client component. Wrap your layout in it, so every page shares one provider, and put a chat on a page:
import { Providers } from "./providers";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}"use client";
import { CopilotChat } from "@copilotkit/react-core/v2";
export default function Page() {
return <CopilotChat />;
}Ask for something visual#
Run npm run dev, open the page, and send:
Compare the Starter, Team, and Enterprise plans in a tableThe table fills in while the agent writes it. It is a real component, so a follow-up such as "add a column for support" changes it in place.
That is a working setup. The agent now renders standard components in the chat.
See this in Inspector
Open Inspector on localhost. Go to Inspect, then Generative UI. You see the catalog, the targets and surfaces on the page, and the text that each run adds to the prompt of the model.
More detail: Inspector.
When something does not render#
The agent answered, but the UI did not show, or it shows the wrong thing. Open Inspector, go to Generative UI, and open the tab for what you see:
| What you see | Open | What to look for |
|---|---|---|
| The pane says "No generative UI" | Nothing to open | The provider has no generativeUI config. Pass generativeUI={{ ui }} to CopilotKitProvider. |
| The surface stays empty, or shows its fallback | Surfaces | Find the surface and open its card. Rejected operations gives the reason for each dropped operation, for example an unknown component. |
| A component shows wrong or missing values | Surfaces | The component tree shows each rendered component with the props that the agent sent. |
| The agent never uses a component or a function | Catalog | Search for its name. A component that is not in the list is not sent to the model. A function with no mounted handler gets a warning. |
| You want to see what the model got | Prompt | The envelope and the render_ui guide of the last run, and of the next run. |
Learn the ideas#
Three ideas carry the whole feature:
- Surfaces: a place where generated UI renders. The chat is one. Any area of your page can be one.
- Catalogs: the components that the agent can use, with their props. Use the standard one, or add your own.
- Transports: how the UI reaches the browser. The agent calls a tool, or it writes the UI in its markdown reply.
Build with it#
- Use the standard catalog: what is in it, and how to drop or replace components.
- Connect app state and actions: show live app data, run your code on a click, and keep what users type.
- Theme the components: your colors and fonts, and dark mode.
- Markdown mode: the agent writes the UI inside its reply, with no tool call.
- Adopt from A2UI: move from the
a2uirenderer, with the v0.9 to 1.0 mapping and a support matrix.
See it in an app#
examples/v2/generative-ui-shop is a shop where the chat changes the page. Ask for "top rated" products, and the featured row on the home page swaps to what the agent picked.