Skip to main content

Overview

The useComponent hook is a convenience wrapper around useFrontendTool that makes it easy to register React components as tools that AI agents can render in chat conversations. It automatically builds the tool description and wiring for you.

Usage

import { useComponent } from "@copilotkit/react";
import { z } from "zod";

function WeatherCard({ city, temperature }: { city: string; temperature: number }) {
  return (
    <div className="weather-card">
      <h3>{city}</h3>
      <p>{temperature}°C</p>
    </div>
  );
}

function MyComponent() {
  useComponent({
    name: "showWeather",
    parameters: z.object({
      city: z.string(),
      temperature: z.number(),
    }),
    render: WeatherCard,
  });
  
  return <div>...</div>;
}

Type Signature

function useComponent<TSchema extends z.ZodTypeAny | undefined>(
  config: {
    name: string;
    description?: string;
    parameters?: TSchema;
    render: ComponentType<InferRenderProps<TSchema>>;
    agentId?: string;
  },
  deps?: ReadonlyArray<unknown>
): void

Parameters

config
ComponentConfig
required
Component configuration object with the following properties:
deps
ReadonlyArray<unknown>
Optional dependency array (like useEffect). When dependencies change, the component is re-registered.
useComponent(config, [theme, locale]);

Return Value

This hook does not return a value. The component is registered as a tool and automatically unregistered on unmount.

Examples

Basic Component

import { useComponent } from "@copilotkit/react";
import { z } from "zod";

function GreetingCard({ message }: { message: string }) {
  return (
    <div className="greeting-card">
      <p>{message}</p>
    </div>
  );
}

function App() {
  useComponent({
    name: "showGreeting",
    parameters: z.object({
      message: z.string().describe("The greeting message to display"),
    }),
    render: GreetingCard,
  });
  
  return <div>App content</div>;
}

Product Card Component

import { useComponent } from "@copilotkit/react";
import { z } from "zod";

interface ProductCardProps {
  name: string;
  price: number;
  image: string;
  inStock: boolean;
}

function ProductCard({ name, price, image, inStock }: ProductCardProps) {
  return (
    <div className="product-card">
      <img src={image} alt={name} />
      <h3>{name}</h3>
      <p className="price">${price}</p>
      <span className={`stock ${inStock ? "in-stock" : "out-of-stock"}`}>
        {inStock ? "In Stock" : "Out of Stock"}
      </span>
    </div>
  );
}

function ProductCatalog() {
  useComponent({
    name: "showProduct",
    description: "Displays a product card with image, pricing, and availability",
    parameters: z.object({
      name: z.string(),
      price: z.number(),
      image: z.string().url(),
      inStock: z.boolean(),
    }),
    render: ProductCard,
  });
  
  return <div>Catalog content</div>;
}

Inline Component

import { useComponent } from "@copilotkit/react";
import { z } from "zod";

function Dashboard() {
  useComponent({
    name: "showMetric",
    description: "Display a key metric card",
    parameters: z.object({
      label: z.string(),
      value: z.number(),
      unit: z.string(),
      trend: z.enum(["up", "down", "stable"]),
    }),
    render: ({ label, value, unit, trend }) => {
      const trendIcon = {
        up: "📈",
        down: "📉",
        stable: "➡️",
      }[trend];
      
      return (
        <div className="metric-card">
          <div className="label">{label}</div>
          <div className="value">
            {value} {unit}
          </div>
          <div className="trend">{trendIcon}</div>
        </div>
      );
    },
  });
  
  return <div>Dashboard content</div>;
}

Component Without Parameters

For simple components that don’t need parameters:
import { useComponent } from "@copilotkit/react";

function LoadingSpinner() {
  return <div className="spinner">Loading...</div>;
}

function App() {
  useComponent({
    name: "showLoading",
    description: "Display a loading spinner",
    render: LoadingSpinner,
    // No parameters needed
  });
  
  return <div>App content</div>;
}

Chart Component

import { useComponent } from "@copilotkit/react";
import { z } from "zod";
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip } from "recharts";

interface ChartData {
  label: string;
  value: number;
}

function Chart({ data, title }: { data: ChartData[]; title: string }) {
  return (
    <div className="chart-container">
      <h3>{title}</h3>
      <BarChart width={400} height={300} data={data}>
        <CartesianGrid strokeDasharray="3 3" />
        <XAxis dataKey="label" />
        <YAxis />
        <Tooltip />
        <Bar dataKey="value" fill="#8884d8" />
      </BarChart>
    </div>
  );
}

function Analytics() {
  useComponent({
    name: "showChart",
    description: "Display a bar chart with provided data",
    parameters: z.object({
      data: z.array(z.object({
        label: z.string(),
        value: z.number(),
      })),
      title: z.string(),
    }),
    render: Chart,
  });
  
  return <div>Analytics dashboard</div>;
}

Agent-Specific Component

import { useComponent } from "@copilotkit/react";
import { z } from "zod";

function ResearchPaperCard({ title, authors, year, abstract }: any) {
  return (
    <div className="paper-card">
      <h3>{title}</h3>
      <p className="authors">{authors.join(", ")}</p>
      <p className="year">{year}</p>
      <p className="abstract">{abstract}</p>
    </div>
  );
}

function ResearchAgent() {
  useComponent({
    name: "showPaper",
    description: "Display academic paper information",
    parameters: z.object({
      title: z.string(),
      authors: z.array(z.string()),
      year: z.number(),
      abstract: z.string(),
    }),
    render: ResearchPaperCard,
    agentId: "research-agent", // Only research agent can use this
  });
  
  return <div>Research interface</div>;
}

List Component

import { useComponent } from "@copilotkit/react";
import { z } from "zod";

interface Task {
  id: string;
  title: string;
  completed: boolean;
  priority: "low" | "medium" | "high";
}

function TaskList({ tasks }: { tasks: Task[] }) {
  return (
    <div className="task-list">
      <h3>Tasks ({tasks.length})</h3>
      <ul>
        {tasks.map((task) => (
          <li key={task.id} className={task.completed ? "completed" : ""}>
            <span className={`priority priority-${task.priority}`}>
              {task.priority}
            </span>
            <span className="title">{task.title}</span>
            {task.completed && <span className="checkmark"></span>}
          </li>
        ))}
      </ul>
    </div>
  );
}

function TaskManager() {
  useComponent({
    name: "showTasks",
    description: "Display a list of tasks with their status and priority",
    parameters: z.object({
      tasks: z.array(z.object({
        id: z.string(),
        title: z.string(),
        completed: z.boolean(),
        priority: z.enum(["low", "medium", "high"]),
      })),
    }),
    render: TaskList,
  });
  
  return <div>Task manager interface</div>;
}

Dynamic Component with Dependencies

import { useComponent } from "@copilotkit/react";
import { z } from "zod";
import { useTheme } from "./hooks/useTheme";

function Alert({ message, type }: { message: string; type: string }) {
  const theme = useTheme();
  
  const colors = {
    info: theme === "dark" ? "#3b82f6" : "#2563eb",
    warning: theme === "dark" ? "#f59e0b" : "#d97706",
    error: theme === "dark" ? "#ef4444" : "#dc2626",
  };
  
  return (
    <div 
      className="alert" 
      style={{ backgroundColor: colors[type as keyof typeof colors] }}
    >
      {message}
    </div>
  );
}

function App() {
  const theme = useTheme();
  
  useComponent({
    name: "showAlert",
    description: "Display an alert message",
    parameters: z.object({
      message: z.string(),
      type: z.enum(["info", "warning", "error"]),
    }),
    render: Alert,
  }, [theme]); // Re-register when theme changes
  
  return <div>App content</div>;
}

Behavior

Auto-Generated Description

The hook automatically generates a description for the tool:
Use this tool to display the "{name}" component in the chat. 
This tool renders a visual UI component for the user.
If you provide an additional description, it is appended:
Use this tool to display the "{name}" component in the chat. 
This tool renders a visual UI component for the user.

{your description}

Type Safety

When parameters is provided, your render component’s props are automatically typed:
// TypeScript knows the exact prop types
useComponent({
  parameters: z.object({
    name: z.string(),
    age: z.number(),
  }),
  render: ({ name, age }) => {
    // name: string, age: number
    return <div>{name} is {age} years old</div>;
  },
});

No Handler Required

Unlike useFrontendTool, useComponent doesn’t require a handler function. It’s purely for rendering UI components based on parameters the agent provides.

Comparison with useFrontendTool

FeatureuseComponentuseFrontendTool
Handler❌ No✅ Yes
Custom rendering✅ Yes✅ Yes (optional)
Auto-description✅ Yes❌ Manual
PurposeDisplay UIExecute logic + UI
Simpler API✅ Yes❌ More config
Use useComponent when:
  • You only need to render UI, no backend logic
  • You want a simpler API for component rendering
  • The agent just needs to display information
Use useFrontendTool when:
  • You need to execute logic when the tool is called
  • You need access to the handler context
  • You want more control over the tool definition

Notes

useComponent is built on top of useFrontendTool. It’s a convenience wrapper that simplifies the common case of rendering components without handler logic.
If you’re passing an inline config object, wrap it in useMemo to avoid unnecessary re-registrations:
const config = useMemo(() => ({
  name: "myComponent",
  parameters: z.object({ ... }),
  render: MyComponent,
}), [/* dependencies */]);

useComponent(config);