CopilotKit

React Native

Get started with CopilotKit in React Native.


@copilotkit/react-native provides a lightweight, headless wrapper around CopilotKit's React hooks for React Native apps. You build the UI — CopilotKit handles the agent communication.

Prerequisites#

  • A CopilotKit runtime endpoint (self-hosted or cloud)
  • React Native 0.70+ (bare CLI or Expo)
  • Node.js 20+

Getting started#

Create your React Native app#

If you don't have one already:

npx @react-native-community/cli@latest init MyCopilotApp
cd MyCopilotApp

Install CopilotKit#

npm install @copilotkit/react-native
pnpm add @copilotkit/react-native
yarn add @copilotkit/react-native

Add polyfills#

React Native's JS runtime (Hermes) lacks several Web APIs that CopilotKit depends on. Import the polyfills before any other code in your entry point:

index.js
import "@copilotkit/react-native/polyfills"; 

import { AppRegistry } from "react-native";
import App from "./App";
import { name as appName } from "./app.json";

AppRegistry.registerComponent(appName, () => App);

Granular polyfills

If you already polyfill some of these APIs (e.g. ReadableStream), you can import only what you need instead:

import "@copilotkit/react-native/polyfills/streams";
import "@copilotkit/react-native/polyfills/encoding";
import "@copilotkit/react-native/polyfills/crypto";
import "@copilotkit/react-native/polyfills/dom";
import "@copilotkit/react-native/polyfills/location";

Wrap your app with CopilotKitProvider#

App.tsx
import { CopilotKitProvider } from "@copilotkit/react-native"; 
import { SafeAreaProvider } from "react-native-safe-area-context";
import { ChatScreen } from "./src/ChatScreen";

export default function App() {
  return (
    <SafeAreaProvider>
      <CopilotKitProvider runtimeUrl="https://your-server/api/copilotkit">
        <ChatScreen />
      </CopilotKitProvider>
    </SafeAreaProvider>
  );
}

Build your chat UI#

Hooks from the web SDK work identically in React Native. Here's a minimal chat screen using useAgent and useCopilotKit:

src/ChatScreen.tsx
import { useCallback, useRef, useState } from "react";
import {
  FlatList,
  KeyboardAvoidingView,
  Platform,
  Text,
  TextInput,
  TouchableOpacity,
  View,
} from "react-native";
import { useAgent, useCopilotKit } from "@copilotkit/react-native"; 

export function ChatScreen() {
  const [inputText, setInputText] = useState("");
  const flatListRef = useRef<FlatList>(null);

  const { copilotkit } = useCopilotKit(); 
  const { agent } = useAgent({ agentId: "default" });

  const messages = agent?.messages ?? [];
  const isLoading = agent?.isRunning ?? false;

  const handleSend = useCallback(async () => {
    const text = inputText.trim();
if (!text || isLoading || !agent) return;

    setInputText("");
    agent.addMessage({ 
      id: `user-${Date.now()}`,
      role: "user",
      content: text,
    });
    await copilotkit.runAgent({ agent }); 
  }, [inputText, isLoading, agent, copilotkit]);

  return (
    <KeyboardAvoidingView
      style={{ flex: 1 }}
      behavior={Platform.OS === "ios" ? "padding" : "height"}
    >
      <FlatList
        ref={flatListRef}
        data={messages.filter(
(m) => m.role === "user" || (m.role === "assistant" && m.content),
        )}
        renderItem={({ item }) => (
          <View style={{ padding: 12, maxWidth: "80%" }}>
            <Text>{item.content}</Text>
          </View>
        )}
        keyExtractor={(item, i) => item.id ?? String(i)}
        onContentSizeChange={() =>
          flatListRef.current?.scrollToEnd({ animated: true })
        }
      />
      <View style={{ flexDirection: "row", padding: 8 }}>
        <TextInput
          style={{ flex: 1, borderWidth: 1, borderRadius: 8, padding: 8 }}
          value={inputText}
          onChangeText={setInputText}
          placeholder="Type a message..."
        />
        <TouchableOpacity onPress={handleSend} style={{ padding: 8 }}>
          <Text>Send</Text>
        </TouchableOpacity>
      </View>
    </KeyboardAvoidingView>
  );
}

Headless by design

@copilotkit/react-native is headless — it provides hooks, not UI components. You have full control over your chat interface using standard React Native components.

Run the app#

npx react-native run-ios
npx react-native run-android

Start chatting!#

Your React Native app is now connected to CopilotKit. Hooks from the web SDK work identically:

  • useAgent — connect to an agent and manage messages
  • useFrontendTool — let the agent call functions in your app
  • useHumanInTheLoop — add approval flows for agent actions
  • useCopilotKit — access the CopilotKit instance directly
Troubleshooting
  • Metro can't resolve modules: Clear the cache with npx react-native start --reset-cache
  • Streaming not working: Make sure polyfills are imported before any CopilotKit code in your entry point
  • Existing polyfill conflicts: Use the granular imports instead of the barrel polyfills import
  • Make sure the runtime endpoint URL is reachable from your device/emulator (use your machine's IP, not localhost, for physical devices)

What's included#

ExportDescription
CopilotKitProviderLightweight provider — no DOM, CSS, or web framework deps
useAgent, useFrontendTool, etc.Re-exported hooks from @copilotkit/react-core
@copilotkit/react-native/polyfillsReadableStream, TextEncoder, crypto, DOMException, location

Known limitations#

  • No pre-built UI components — you build the chat interface with React Native components
  • Markdown rendering — assistant messages with markdown render as plain text; bring your own renderer (e.g. react-native-markdown-display)
  • Voice@copilotkit/voice has not been adapted for React Native yet