The deprecated `useRenderTool` shim landed after these pages were written, so they described a harder break than the code delivers — "React Native has no render-tool API of its own", and a migration table presenting the old hook as simply gone. reference/react-native/hooks/useRenderTool: - The overview and the migration narrative now say what actually happens: the name still exists, still works this release, routes by shape, warns, and goes away in the next minor. The routing table is the shim's three rules verbatim (src/hooks/useRenderTool.ts `routeFor`), including the unconditional wildcard and why it is unconditional. - The warning is quoted from the source string rather than paraphrased, and gets its own "What the warning does not cover" section: dev-only gate, per-name dedup for the module's lifetime, silent when no old field is present, and emitted from an effect so an unmounted screen never warns. - "Three shapes the compiler does not catch" is kept, not deleted. It is now framed as what the shim exists to route — and as what to audit by hand anyway, because the notice is development-only and because all three go back to degrading silently once the shim is removed. - Two new migration rows for losses the table omitted: bare `RenderToolProps` is now `TS2314` (core's `S` has no default where RN's `T` did), and the `args` -> `parameters` render-prop rename is still `TS2339` even through the shim — verified with tsc, the shim restores the old CONFIG fields only. The `RenderToolFunction<T>` row now points somewhere instead of saying "gone". - `parameters` was documented as `Partial<T> | T`, using a generic this page renamed to `S` and explicitly warns is the schema rather than the arguments. It is `Partial<InferSchemaOutput<S>> | InferSchemaOutput<S>` (react-core/src/v2/hooks/use-render-tool.tsx:9-31). reference/react-native/hooks/useFrontendTool had zero mentions of `ReactElement`, `ReactNode` or `FlatList` despite being where the migration sends people. It now documents that its `render` is a `React.ComponentType` and therefore accepts a bare string that throws on a device, and points at the opt-in `FrontendToolRenderFunction<T>`. docs/frontends/react-native and packages/react-native/USAGE.md (which ships in the npm tarball — no `files` array, no `.npmignore`) get the same corrections at their own length. Every code sample added here was typechecked by pasting it into the package and running `tsc --noEmit`, including the ones asserted to FAIL. Internal links and heading anchors were checked mechanically against the content tree.
6.9 KiB
@copilotkit/react-native — Usage
Prerequisites
Install all required peer dependencies:
npm install react react-native @gorhom/bottom-sheet react-native-gesture-handler react-native-reanimated react-native-streamdown
@gorhom/bottom-sheet, react-native-gesture-handler, react-native-reanimated, and react-native-streamdown are required peer dependencies for the UI components.
Quick Start
import "@copilotkit/react-native/polyfills";
import {
CopilotKitProvider,
CopilotChat,
useFrontendTool,
} from "@copilotkit/react-native";
import { z } from "zod";
function App() {
return (
<CopilotKitProvider runtimeUrl="https://your-server/api/copilotkit">
<ChatScreen />
</CopilotKitProvider>
);
}
function ChatScreen() {
// parameters accepts any StandardSchemaV1-compatible schema (Zod, Valibot, ArkType, etc.)
useFrontendTool({
name: "showWeather",
description: "Show weather info",
parameters: z.object({ city: z.string() }),
render: ({ args }) => <WeatherCard city={args.city} />,
});
return <CopilotChat placeholder="Ask anything..." />;
}
Available Components
CopilotChat
Inline chat panel. Renders a message list with an input bar.
import { CopilotChat } from "@copilotkit/react-native";
<CopilotChat placeholder="Type a message..." />;
CopilotModal
Modal chat overlay. Open/close programmatically via a ref.
import { CopilotModal, type CopilotModalRef } from "@copilotkit/react-native";
import { useRef } from "react";
const modalRef = useRef<CopilotModalRef>(null);
<CopilotModal ref={modalRef} headerTitle="Assistant" />;
// Open it:
modalRef.current?.open();
CopilotMarkdown
Renders Markdown text with sensible React Native styling.
import { CopilotMarkdown } from "@copilotkit/react-native";
<CopilotMarkdown content="**Hello** from CopilotKit!" />;
AssistantMessage / UserMessage
Individual message bubbles. Useful when building a custom chat UI.
import { AssistantMessage, UserMessage } from "@copilotkit/react-native";
<UserMessage content="What's the weather?" />
<AssistantMessage content="It's sunny!" isLoading={false} />
Hooks
The package re-exports react-core's hooks. The two that draw tool calls are worth telling apart.
useFrontendTool
Registers a tool and, optionally, its renderer. The tool is advertised to the
model on every run, so it takes a description and (if it should do something on
the device) a handler. Render props carry the parsed arguments as args.
// parameters accepts any StandardSchemaV1-compatible schema (Zod, Valibot, ArkType, etc.)
useFrontendTool({
name: "showChart",
description: "Display a chart",
parameters: z.object({ data: z.record(z.unknown()) }),
render: ({ args }) => <ChartView data={args.data} />,
});
Its render is a React.ComponentType, so the return type is ReactNode and a
bare string typechecks — then throws Text strings must be rendered within a
<Text> component on a device. FrontendToolRenderFunction<T> is an opt-in
type that narrows the return to ReactElement | null; annotate the renderer with
it and the compiler rejects the string:
import type { FrontendToolRenderFunction } from "@copilotkit/react-native";
const renderChart: FrontendToolRenderFunction<{
data: Record<string, unknown>;
}> = ({ args }) => <ChartView data={args.data ?? {}} />;
useFrontendTool({
name: "showChart",
description: "Display a chart",
parameters: z.object({ data: z.record(z.unknown()) }),
render: renderChart,
});
useRenderTool
Registers a renderer only — nothing is advertised to the model and nothing
becomes callable. Use it to draw a tool call somebody else owns, such as a
server-side tool. Render props carry the parsed arguments as parameters, and
parameters is required on a named renderer. render is already narrowed to
ReactElement | null here, so no annotation is needed.
useRenderTool({
name: "showChart",
parameters: z.object({ data: z.record(z.unknown()) }),
render: ({ status, parameters }) => {
// `parameters` is Partial while the agent is still writing the call.
if (status === "inProgress") return <Text>Preparing…</Text>;
return <ChartView data={parameters.data} />;
},
});
name: "*" registers a fallback for every tool call with no renderer of its own,
and is the one case that takes no schema:
useRenderTool({
name: "*",
render: ({ name, status }) => <Text>{`${name}: ${status}`}</Text>,
});
Deprecated on React Native, for one release. React Native used to export a
different hook under this name — one that registered a tool as well as a
renderer — so useRenderTool here is currently a compatibility shim over both
react-core hooks, scheduled for removal in the next minor. Your existing call
still works: name: "*" always registers a renderer only, and any other name
carrying description or handler is routed to useFrontendTool the way the
old hook did. Either way it warns in development (dev only, once per tool name)
and tells you what to rename the call to. One thing does not carry over: on a
named renderer the render props are parameters, not args, so a typed
render: ({ args }) => … fails with TS2339 (the wildcard's props are untyped,
so it still compiles there).
See the useRenderTool reference
for the routing rules, the warnings, and the full migration table.
Alternative Import Path
Components can also be imported from the /components subpath:
import { CopilotChat, CopilotModal } from "@copilotkit/react-native/components";
Headless Import Path (custom UI, no chat/attachment native deps)
If you build a fully custom chat UI and only need the provider and the
agent/tool hooks, import from @copilotkit/react-native/headless:
import {
CopilotKitProvider,
useAgent,
useFrontendTool,
useRenderTool,
} from "@copilotkit/react-native/headless";
The default barrel (@copilotkit/react-native) statically re-exports the
prebuilt chat components (CopilotChat / CopilotModal / CopilotSidebar /
CopilotPopup, which import @gorhom/bottom-sheet) and useAttachments (which
imports expo-document-picker + expo-file-system). Even though those are
optional peer dependencies, the static re-export forces Metro to resolve them at
bundle time — so a headless consumer previously had to install every chat and
attachment native dep, or stub them in metro.config.js, to get past
Unable to resolve module expo-document-picker.
The /headless entry re-exports only the provider, the platform-agnostic hooks,
the render-tool registry, and the core/AG-UI types — none of the chat UI or
useAttachments — so those native deps never enter the bundle graph and the
metro.config.js stub workaround is no longer needed. Polyfills are still
auto-installed, so no separate import "@copilotkit/react-native/polyfills" is
required.