useFrontendTool

React hook for registering client-side tool handlers with optional UI rendering in React Native


Overview

useFrontendTool registers a client-side tool with CopilotKit at component scope. When the agent decides to call the tool, the provided handler function executes on the device. Optionally, you can supply a render component to display custom UI in the chat showing the tool's execution progress and results.

Re-exported from @copilotkit/react-core/v2. It is identical to the React (V2) useFrontendTool; only the import path differs.

The hook manages the full registration lifecycle: it warns if a tool with the same name already exists, registers the tool and its render component on mount, and cleans up both registrations on unmount. In v2, parameter schemas are defined using Zod instead of plain parameter arrays.

Signature

import { useFrontendTool } from "@copilotkit/react-native";

function useFrontendTool<T extends Record<string, unknown>>(
  tool: ReactFrontendTool<T>,
  deps?: ReadonlyArray<unknown>,
): void;

Parameters

Prop

Type

Prop

Type

Usage

Basic Tool with Zod Parameters

import { useState } from "react";
import { Text, View } from "react-native";
import { useFrontendTool } from "@copilotkit/react-native";
import { z } from "zod";

function TodoManager() {
  const [todos, setTodos] = useState<string[]>([]);

  useFrontendTool(
    {
      name: "addTodo",
      description: "Add a new item to the user's todo list",
      parameters: z.object({
        text: z.string().describe("The todo item text"),
        priority: z.enum(["low", "medium", "high"]).describe("Priority level"),
      }),
      handler: async ({ text, priority }) => {
        setTodos((prev) => [...prev, text]);
        return `Added "${text}" with ${priority} priority`;
      },
    },
    [],
  );

  return (
    <View>
      {todos.map((t, i) => (
        <Text key={i}>{t}</Text>
      ))}
    </View>
  );
}

Tool with Custom Render Component

import { Text, View } from "react-native";
import { useFrontendTool, ToolCallStatus } from "@copilotkit/react-native";
import { z } from "zod";

function WeatherWidget() {
  useFrontendTool(
    {
      name: "getWeather",
      description: "Fetch and display weather information for a city",
      parameters: z.object({
        city: z.string().describe("City name"),
        units: z.enum(["celsius", "fahrenheit"]).default("celsius"),
      }),
      handler: async ({ city, units }, { signal }) => {
        const response = await fetch(
          `https://your-server/api/weather?city=${city}&units=${units}`,
          { signal },
        );
        const data = await response.json();
        return JSON.stringify(data);
      },
      render: ({ args, status, result }) => {
        if (status === ToolCallStatus.InProgress) {
          return (
            <View>
              <Text>Fetching weather for {args.city}...</Text>
            </View>
          );
        }
        if (status === ToolCallStatus.Complete && result) {
          const data = JSON.parse(result);
          return (
            <View>
              <Text>{data.city}</Text>
              <Text>
                {data.temperature}&deg; {data.units}
              </Text>
              <Text>{data.conditions}</Text>
            </View>
          );
        }
        return null;
      },
    },
    [],
  );

  return null;
}

Conditionally Available Tool

import { View } from "react-native";
import { useFrontendTool } from "@copilotkit/react-native";
import { z } from "zod";

function AdminPanel({ isAdmin }: { isAdmin: boolean }) {
  useFrontendTool(
    {
      name: "deleteUser",
      description: "Delete a user account by ID (admin only)",
      parameters: z.object({
        userId: z.string().describe("The ID of the user to delete"),
      }),
      handler: async ({ userId }) => {
        await fetch(`https://your-server/api/users/${userId}`, {
          method: "DELETE",
        });
        return `User ${userId} deleted`;
      },
      available: isAdmin ? "enabled" : "disabled",
    },
    [isAdmin],
  );

  return <View>{/* admin UI */}</View>;
}

Behavior

  • Duplicate detection: If a tool with the same name is already registered, the hook logs a warning. Only one tool per name is active at a time.
  • Mount/Unmount lifecycle: The tool and its optional render component are registered on mount and removed on unmount.
  • Dependency tracking: When deps is provided, the tool registration is refreshed whenever any dependency value changes, similar to useEffect.
  • Render component lifecycle: If a render function is provided, it is added to the internal render tool calls registry. It receives streaming args (partial during InProgress, complete during Executing and Complete).
  • No return value: The hook returns void.

Related