Components as Tools

Let your agent render rich Vue components directly in the chat by calling them as tools.

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.

What is this?#

Tool-based Generative UI is the simplest form of Generative UI: you register a Vue component with useComponent, and CopilotKit exposes it to the agent as a tool. When the agent calls the tool, CopilotKit renders your component inline in the chat, passing the tool's arguments straight through as typed props.

Unlike tool rendering, which wraps a real backend tool in a custom UI, tool-based GenUI is the component. There is no handler, no user interaction, no server-side execution. The agent decides when to show it, populates the data, and CopilotKit paints it.

When should I use this?#

Use useComponent when you want to:

  • Display rich UI (cards, charts, tables, dashboards) inline in the chat
  • Show structured data the agent has derived from its reasoning
  • Render previews, status indicators, or visual summaries
  • Let the agent present information beyond plain text

For components that need user interaction, see Human-in-the-loop. For operational transparency around a real backend tool, see Tool rendering.

How it works in code#

useComponent takes a name, a Zod schema for its props, and the component to render. The runtime advertises that schema when it registers the frontend tool, and TypeScript infers the component props from it. Runtime arguments are JSON-parsed but are not validated against the schema.

src/components/WeatherCard.vue
<script setup lang="ts">
defineProps<{ city: string; temperature: number; condition: string }>();
</script>

<template>
  <article>
    <strong>{{ city }}</strong>
    <p>{{ temperature }}° · {{ condition }}</p>
  </article>
</template>

Vue composables execute during component setup, so the component calling useComponent must be a descendant of CopilotKitProvider.

src/components/AgentExperience.vue
<script setup lang="ts">
import { z } from "zod";
import { CopilotChat, useComponent } from "@copilotkit/vue/v2";
import WeatherCard from "./WeatherCard.vue";

useComponent({
  name: "show_weather",
  description: "Display the weather for one city.",
  parameters: z.object({
    city: z.string(),
    temperature: z.number(),
    condition: z.string(),
  }),
  render: WeatherCard,
});
</script>

<template><CopilotChat /></template>
src/App.vue
<script setup lang="ts">
import { CopilotKitProvider } from "@copilotkit/vue/v2";
import AgentExperience from "./components/AgentExperience.vue";
</script>

<template>
  <CopilotKitProvider runtime-url="/api/copilotkit">
    <AgentExperience />
  </CopilotKitProvider>
</template>

The component itself is ordinary Vue: it reads only its props and can stream in as the agent fills the payload. It doesn't know anything about CopilotKit.

The name you pass to useComponent is what the agent sees as the tool name. Make it a verb like render_bar_chart or show_weather so the LLM reliably picks it when the user asks for that visualization.

The model supplies the props, so validate consequential data against application state rather than treating a well-formed card as proof that the values are authorized or current.

Choose your AI backend

See Integrations for all available frameworks (generative-ui/tool-based).