useRenderTool
Register typed Vue renderers for tool calls, by tool name or wildcard
Overview
useRenderTool is a Vue 3 composable that registers a chat renderer for tool calls.
You can target a specific tool name (with a typed Zod parameters schema) or use a wildcard ("*") fallback renderer.
The render target is a Vue render function (returning a VNodeChild) or a Vue Component. It receives the tool name, the parsed parameters, the current status, and the result (when complete). This is not JSX; you render with Vue's h(), a setup-defined render function, or by passing a single-file Vue component.
This composable only handles rendering. It does not register a frontend handler.
Call useRenderTool from inside setup() (or <script setup>) of a component mounted under CopilotKitProvider. It reads the active CopilotKit instance from provider context, so it cannot be called outside that tree.
Import
import { useRenderTool } from "@copilotkit/vue/v2";Signature
Wildcard overload
import { useRenderTool } from "@copilotkit/vue/v2";
import type { WatchSource } from "vue";
useRenderTool(
{
name: "*",
render: ((props) => VNodeChild) | Component,
agentId?: string,
},
deps?: WatchSource<unknown>[],
): void;Named overload
import { z } from "zod";
import { useRenderTool } from "@copilotkit/vue/v2";
import type { WatchSource } from "vue";
useRenderTool<S>(
{
name: string,
parameters: S, // a Zod / Standard Schema; parameters are inferred from it
render: ((props: RenderToolProps<S>) => VNodeChild) | Component<RenderToolProps<S>>,
agentId?: string,
},
deps?: WatchSource<unknown>[],
): void;Parameters
Prop
Type
Prop
Type
Render Props
render receives a discriminated union (RenderToolProps) keyed on status. name and toolCallId are present in all states.
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
The three states are:
{ status: "inProgress", parameters: Partial<T>, result: undefined }-- arguments are still being streamed.{ status: "executing", parameters: T, result: undefined }-- arguments are fully resolved; the tool is executing.{ status: "complete", parameters: T, result: string }-- execution finished and a result is available.
Behavior
- Builds a renderer entry with
defineToolCallRendererand registers it on the active CopilotKit instance viaaddHookRenderToolCall. - Maps the internal
argsprop ontoparametersbefore callingrender, so your render function always seesparameters. - Deduplicates by
agentId:namekey (latest registration wins). - Intentionally does not remove the renderer on cleanup, so previous chat history can still render.
- Re-registers reactively (via
watch,immediate: true) whenname,agentId, or any value indepschanges. Include changing reactive captures indeps.
Usage
Named tool renderer (render function)
<script setup lang="ts">
import { h } from "vue";
import { z } from "zod";
import { useRenderTool } from "@copilotkit/vue/v2";
useRenderTool({
name: "searchDocs",
parameters: z.object({ query: z.string() }),
render: (props) => {
if (props.status === "inProgress") {
return h("div", `Preparing ${props.name}...`);
}
if (props.status === "executing") {
return h("div", `Searching for: ${props.parameters.query}`);
}
return h("div", `Done: ${props.result}`);
},
});
</script>
<template>
<slot />
</template>Named tool renderer (Vue component)
Pass a Vue component instead of a render function. The render props become the component's props.
<!-- SearchDocsRenderer.vue -->
<script setup lang="ts">
defineProps<{
name: string;
toolCallId: string;
status: "inProgress" | "executing" | "complete";
parameters: { query?: string };
result?: string;
}>();
</script>
<template>
<div v-if="status === 'inProgress'">Preparing {{ name }}...</div>
<div v-else-if="status === 'executing'">Searching for: {{ parameters.query }}</div>
<div v-else>Done: {{ result }}</div>
</template><!-- App.vue -->
<script setup lang="ts">
import { z } from "zod";
import { useRenderTool } from "@copilotkit/vue/v2";
import SearchDocsRenderer from "./SearchDocsRenderer.vue";
useRenderTool({
name: "searchDocs",
parameters: z.object({ query: z.string() }),
render: SearchDocsRenderer,
});
</script>
<template>
<slot />
</template>Wildcard fallback renderer
Omit parameters and use name: "*" to provide a generic UI for any unhandled tool call.
<script setup lang="ts">
import { h } from "vue";
import { useRenderTool } from "@copilotkit/vue/v2";
useRenderTool({
name: "*",
render: (props) =>
h("div", `${props.status === "complete" ? "done" : "running"} ${props.name}`),
});
</script>
<template>
<slot />
</template>Reacting to changing values with deps
deps are Vue WatchSource values (refs or getters), not a dependency array. Pass them when render reads reactive state so the renderer re-registers on change.
<script setup lang="ts">
import { h, ref } from "vue";
import { z } from "zod";
import { useRenderTool } from "@copilotkit/vue/v2";
const locale = ref("en");
useRenderTool(
{
name: "searchDocs",
parameters: z.object({ query: z.string() }),
render: (props) =>
h("div", `[${locale.value}] ${props.name}: ${props.status}`),
},
[locale],
);
</script>
<template>
<slot />
</template>Related
defineToolCallRenderer-- build a renderer entry directlyuseFrontendTool-- register tools with handlersCopilotKitProvider-- provide the CopilotKit instance to the tree