Save your spot

Live eventA single Agent, 3 surfaces

React Native: Your Agent, inside Your Mobile App

By Nathan Tarbert and Anmol Baranwal
August 19, 2026

React Native AI Agents with CopilotKit: Setup, Tools, and Generative UI

You can now bring any AG-UI agent into an iOS or Android app!

@copilotkit/react-native is the AG-UI client for React Native. Same agent, same runtime, same tool protocol as the web SDK, rendering into View and Text instead of the DOM. Not a webview.

So the agent you already run on the web now runs on a phone, with frontend tools executing on the device itself.

The setup

You need React Native 0.70+ (bare CLI or Expo), Node.js 20+, and a model provider API key.

npm install @copilotkit/react-native @copilotkit/runtime
npm install react-native-get-random-values

React Native runs on Hermes, which is missing several Web APIs CopilotKit depends on, so the package installs polyfills for them on first import. Two imports still belong at the very top of your entry point:

// index.js
import "react-native-get-random-values"; // secure RNG, must be first
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);

That order matters. CopilotKit's crypto polyfill falls back to Math.random, and whichever implementation claims crypto.getRandomValues first wins permanently. Put the RNG on line one and you get the secure one.

Stand up the runtime

import { createServer } from "node:http";
import { BuiltInAgent, CopilotRuntime } from "@copilotkit/runtime/v2";
import { createCopilotNodeListener } from "@copilotkit/runtime/v2/node";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({
      model: "openai:gpt-5-mini",
      prompt: "You are a helpful assistant for a React Native app.",
    }),
  },
});

const port = Number(process.env.PORT ?? 8200);

createServer(
  createCopilotNodeListener({
    runtime,
    basePath: "/api/copilotkit",
    cors: true,
  }),
).listen(port, () => {
  console.log(`Copilot Runtime listening at http://localhost:${port}/api/copilotkit`);
});

Then point the provider at it. The emulator address differs per platform, so branch on Platform.OS:

import { CopilotKitProvider } from "@copilotkit/react-native/headless";
import { Platform } from "react-native";
import { ChatScreen } from "./src/ChatScreen";

const runtimeUrl =
  Platform.OS === "android"
    ? "http://10.0.2.2:8200/api/copilotkit"
    : "http://localhost:8200/api/copilotkit";

export default function App() {
  return (
    <CopilotKitProvider runtimeUrl={runtimeUrl}>
      <ChatScreen />
    </CopilotKitProvider>
  );
}

You self-host the runtime on React Native, which also means your model keys and your prompt stay on your own server. Any OpenAI-compatible endpoint works.

Pick your import surface

There are three entry points:

  • @copilotkit/react-native/headless for provider and hooks, with no native dependencies
  • @copilotkit/react-native for the above plus useAttachments and the chat wrappers
  • @copilotkit/react-native/components for the rendered chat UI

Metro, React Native's bundler, pulls in everything a surface depends on, even the parts you never call. Import the root barrel and it drags in the attachment picker, so your release build fails with Unable to resolve module expo-document-picker for a package you never installed.

Building custom UI? Use /headless. It has no native dependencies, so there's nothing for Metro to go looking for. You get the provider and every hook, so the agent, tools, and streaming all work exactly the same, you just draw the interface yourself with plain React Native components (View, Text, etc).

Tools and generative UI

This is where mobile stops being the lesser target. Frontend tools register on the device, so the model's call lands in a handler running on the phone, next to your state and your platform APIs.

For UI drawn inline in the chat, reach for useRenderTool. It's the React-Native-typed hook, and its render is required to return ReactElement | null:

import { ToolCallStatus, useRenderTool } from "@copilotkit/react-native/headless";
import { z } from "zod";

useRenderTool({
  name: "composeStage",
  description: "Render POI panels on the screen.",
  parameters: z.object({ mode: z.string() }),
  render: ({ args, status }) => (
    <StagePreview mode={args.mode} pending={status !== ToolCallStatus.Complete} />
  ),
});

If you'd rather the agent push state into your own reducer and let your views render it, use useFrontendTool with a handler instead. That's the pattern that feels most native on mobile.

Human-in-the-loop, interrupts, suggestions, threads, and agent context all have hooks here too, so approval flows work the same as they do on the web.

Good to know

  • Physical devices block plaintext HTTP by default, so add the cleartext exception in app.json for a LAN dev runtime, or terminate TLS.
  • Voice is on the roadmap and currently bring-your-own. @copilotkit/voice targets the web, and the docs walk through wiring a native recognizer to the same turn-sending path.

Whats next?

A2UI is the thread to watch. It's Google's declarative generative UI spec, with CopilotKit as a launch and design partner, and it inverts the model: the agent emits JSON against a component catalog you control, and only catalog components get constructed.

It's streaming-first and platform-agnostic by design, which is exactly the property that makes it interesting for a View tree. It ships on the web today, and you can design schemas against it in the A2UI Composer.

Getting started

Want to bring CopilotKit into your stack? Talk to our engineers and we'll help you set it up.

Follow CopilotKit on Twitter for updates. If you get stuck, reach out in the CopilotKit or AG-UI communities.

Are you ready?

Stay in the know

Subscribe to our blog and get updates on CopilotKit in your inbox.