showcase/shell-docs/src/content/reference/vue/hooks/useComponent.mdx
useComponent is a convenience composable built on top of useFrontendTool. It registers a tool and renders a Vue component in chat using the tool call parameters as the component's props.
Use this when you want a visual component renderer without writing a full frontend tool config manually. For lower-level control over render status (in progress, executing, complete) or to render any tool with a wildcard, reach for useRenderTool instead.
import type { Component, WatchSource } from "vue";
import type { StandardSchemaV1 } from "@copilotkit/shared";
import { useComponent } from "@copilotkit/vue/v2";
function useComponent<TSchema extends StandardSchemaV1 | undefined = undefined>(
config: {
name: string;
description?: string;
parameters?: TSchema;
// When parameters is provided, props are inferred from the schema output.
// When omitted, the component accepts any props.
render: Component<NoInfer<InferRenderProps<TSchema>>>;
agentId?: string;
},
deps?: WatchSource<unknown>[],
): void;
description, if provided, is appended after it.useFrontendTool. It calls useFrontendTool internally with a render function that mounts your component (via Vue's h) and spreads the tool's args as the component's props.useFrontendTool behavior: a duplicate-name registration logs an override warning and replaces the previous tool, and the tool is removed automatically when the owning scope is disposed.config.name and the deps watch sources. Pass any changing reactive values you depend on through deps.Define a Zod schema for the parameters, a Vue component that receives them as props, and register both with useComponent.
<!-- WeatherCard.vue -->
<script setup lang="ts">
defineProps<{
city: string;
unit: "c" | "f";
}>();
</script>
<template>
<div class="rounded border p-3">
<div class="text-sm text-zinc-500">Weather request</div>
<div class="font-medium">{{ city }} ({{ unit.toUpperCase() }})</div>
</div>
</template>
<!-- App.vue -->
<script setup lang="ts">
import { z } from "zod";
import { useComponent } from "@copilotkit/vue/v2";
import WeatherCard from "./WeatherCard.vue";
const weatherCardSchema = z.object({
city: z.string().describe("City name"),
unit: z.enum(["c", "f"]).default("c"),
});
useComponent({
name: "showWeatherCard",
description: "Render a weather card in chat for the requested city.",
parameters: weatherCardSchema,
render: WeatherCard,
});
</script>
When you omit parameters, the component accepts any props and the agent can invoke the tool with no arguments.
<script setup lang="ts">
import { useComponent } from "@copilotkit/vue/v2";
import LoadingSpinner from "./LoadingSpinner.vue";
useComponent({
name: "showSpinner",
render: LoadingSpinner,
});
</script>
Pass reactive watch sources through deps so the registration re-runs when captured values change.
<script setup lang="ts">
import { ref } from "vue";
import { z } from "zod";
import { useComponent } from "@copilotkit/vue/v2";
import WeatherCard from "./WeatherCard.vue";
// Pass any reactive watch sources the registration should track through `deps`;
// the tool is re-registered whenever one of them changes.
const refreshKey = ref(0);
useComponent(
{
name: "showWeatherCard",
parameters: z.object({ city: z.string() }),
render: WeatherCard,
},
[refreshKey],
);
</script>