CopilotKit

Display components

Register React components that your agent can render in the chat.


"use client";import {  CopilotKitProvider,  CopilotChat,  useComponent,} from "@copilotkit/react-core/v2";export default function GenUiToolBased() {  return (    <CopilotKitProvider runtimeUrl="/api/copilotkit" useSingleEndpoint>      <Demo />    </CopilotKitProvider>  );}function Demo() {  useComponent({    name: "generate_haiku",    render: HaikuCard,  });  return (    <main className="p-8">      <h1 className="text-2xl font-semibold mb-4">Tool-Based Generative UI</h1>      <p className="text-sm opacity-70 mb-6">        Try: &ldquo;Write me a haiku about nature.&rdquo; The agent calls the        <code className="mx-1 px-1 bg-gray-100 rounded">generate_haiku</code>        tool and the result renders inline as a typed card.      </p>      <CopilotChat />    </main>  );}/** * HaikuCard — rendered by `useComponent({ name: "generate_haiku" })`. * * `useComponent` passes tool-call arguments directly as React props (via * `useFrontendTool`'s `render: ({ args }) => <Component {...args} />`). * The D5 fixture sends: { japanese, english, image_name, gradient }. */// eslint-disable-next-line @typescript-eslint/no-explicit-anyfunction HaikuCard(props: any) {  const japaneseLines: string[] = props.japanese ?? [];  const englishLines: string[] = props.english ?? props.lines ?? [];  const gradient: string | undefined = props.gradient;  const topic: string | undefined = props.topic;  const hasContent = japaneseLines.length > 0 || englishLines.length > 0;  if (!hasContent) {    return (      <div        data-testid="haiku-card"        className="border rounded p-3 my-2 opacity-70 text-sm"      >        Composing haiku{topic ? ` about ${topic}` : ""}…      </div>    );  }  return (    <div      data-testid="haiku-card"      className="border rounded p-4 my-2 bg-amber-50"      style={gradient ? { background: gradient } : undefined}    >      <div className="font-medium mb-2">Haiku{topic ? ` — ${topic}` : ""}</div>      {japaneseLines.length > 0 && (        <div className="mb-2">          {japaneseLines.map((line: string, i: number) => (            <div              key={i}              data-testid="haiku-japanese-line"              className="text-lg leading-relaxed"            >              {line}            </div>          ))}        </div>      )}      <div className="font-serif italic whitespace-pre-line text-lg leading-relaxed">        {englishLines.map((line: string, i: number) => (          <div key={i} data-testid="haiku-english-line">            {line}          </div>        ))}      </div>    </div>  );}

What is this?#

Render-only generative UI lets you register React components as tools your agent can invoke. When the agent calls the tool, CopilotKit renders your component directly in the chat with the tool's arguments as props; no handler logic or user interaction required.


useComponent({
name: "showChart",
description: "Populate data and show the user a chart",
parameters: ChartProps,
render: Chart
});

export const ChartProps = z.object({
  title: z.string(),
  data: z.array(z.object({ label: z.string(), value: z.number() })),
});

export function Chart({ title, data }: z.infer<typeof ChartProps>) {
  return (
    <div>
      <h3>{title}</h3>
      <ResponsiveContainer width="100%" height={300}>
        <BarChart data={data}>
          <XAxis dataKey="label" /><YAxis /><Tooltip />
          <Bar dataKey="value" fill="#6366f1" />
        </BarChart>
      </ResponsiveContainer>
    </div>
  );
}

When should I use this?#

Use render-only generative UI when you want to:

  • Display rich UI (cards, charts, tables) inline in the chat
  • Show structured data from agent responses
  • Render previews, status indicators, or visual feedback
  • Let the agent present information beyond plain text

How it works in code#

The renderer component receives the tool's arguments as typed props and mounts inline in the chat. Below is the chart renderer wired up in the canonical demo — the agent emits the data, the component draws it.

bar-chart-renderer.snippet.tsx
import { useComponent } from "@copilotkit/react-core/v2";import { z } from "zod";// Stand-ins for the locally-authored bar chart component + its prop// schema. In a real page, these live in the demo directory (e.g.// `./bar-chart.tsx` exporting `BarChart` and `barChartPropsSchema`).declare const BarChart: React.ComponentType<{  title: string;  data: { label: string; value: number }[];}>;declare const barChartPropsSchema: z.ZodSchema;export function BarChartRenderer() {  useComponent({    name: "render_bar_chart",    description: "Display a bar chart with labeled numeric values.",    parameters: barChartPropsSchema,    render: BarChart,  });