Skip to main content
skill CX Codex

react-native · assistant-ui — Claude Code skill: assistant-ui chat in Expo

A skill for building assistant-ui chat surfaces in Expo and bare React Native: native primitives, the AI SDK runtime and a transport to your backend.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add assistant-ui/skills --skill react-native -a claude-code
Open on GitHub

What react-native does

The skill covers building a mobile chat on @assistant-ui/react-native and @assistant-ui/ai-sdk. The first package ships unstyled primitives wired to the runtime, the second brings the AI SDK v7 runtime and transport. You compose the surface from View, Text, Pressable, TextInput and FlatList.

The model route lives in a separate backend project, never inside the Expo bundle. The app posts UI messages through AssistantChatTransport, the route converts them asynchronously and returns a UI message stream. The endpoint has to be absolute: a physical device cannot resolve its own localhost to your dev server, so a relative URL fails on device. Keep the address in EXPO_PUBLIC_CHAT_ENDPOINT_URL.

Inside you get a quick start from the with-expo example, manual package install, thread and composer composition, generative toolkits through Metro withAui, and a list of common gotchas. MessagePrimitive.Content renders text parts as native Text by default rather than Markdown, so Markdown from the model needs a custom renderer. Web setup and DOM primitive work is routed to the neighbouring ../setup/SKILL.md and ../primitives/SKILL.md.

What it helps with

  • build a chat screen in Expo with native primitives
  • wire the AI SDK v7 runtime into a mobile UI
  • fix a relative endpoint URL that fails on device
  • get Metro to compile files starting with "use generative"
  • move a web chat surface to iOS and Android

Example prompts for the agent

  • Set up an assistant-ui chat in my Expo app using the native primitives
  • Why does my relative chat endpoint URL fail on a physical device?
  • Metro won't compile my generative toolkit — wrap the config with withAui

How to install react-native

1Skills CLI (Vercel)

npx skills add assistant-ui/skills --skill react-native -a claude-code

2Manually: put the files in place

~/.claude/skills/react-native/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/react-native/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about react-native

How do I install the react-native skill in Claude?

Run npx skills add assistant-ui/skills --skill react-native -a claude-code. The skill file lands at ~/.claude/skills/react-native/SKILL.md.

What does the react-native skill do?

It helps you build an assistant-ui chat in Expo or bare React Native: native primitives, the AI SDK v7 runtime and transport, generative toolkits, and migrating a web chat to mobile.

Why must the chat endpoint be an absolute URL?

A physical device cannot resolve its own localhost to your development server. Set the full address and keep it in EXPO_PUBLIC_CHAT_ENDPOINT_URL.

Source text

SKILL.md · MIT
---
name: react-native
description: "Build assistant-ui chat experiences for Expo and bare React Native with `@assistant-ui/react-native` and `@assistant-ui/ai-sdk`. Use when setting up an absolute mobile chat endpoint, `AssistantRuntimeProvider`, `useChatRuntime`, `AssistantChatTransport`, native `ThreadPrimitive`, `ComposerPrimitive`, `MessagePrimitive`, thread lists, attachments, React Native styles, `Metro` `withAui` for a `\"use generative\"` toolkit, local or remote thread adapters, or migrating a web chat surface to iOS and Android. Route here when a relative API URL fails on device, a native primitive lacks runtime state, `Metro` does not compile a toolkit, or web Elements are being used in a native app. Route web setup and DOM primitive work to `../setup/SKILL.md` and `../primitives/SKILL.md`."
license: MIT
---

# assistant-ui React Native

**Always consult [assistant-ui.com/llms.txt](https://www.assistant-ui.com/llms.txt) for the latest API.**

`@assistant-ui/react-native` supplies runtime-connected, unstyled React Native primitives. Use them with `View`, `Text`, `Pressable`, `TextInput`, `FlatList`, and native styles to build the chat surface. `@assistant-ui/ai-sdk` supplies the AI SDK v7 runtime and transport. The model route runs in a separate backend project, never inside the Expo bundle.

## Contents

- [References](#references) | [Quick start](#quick-start) | [Manual setup](#manual-setup) | [Native chat composition](#native-chat-composition) | [Generative toolkits](#generative-toolkits) | [Common Gotchas](#common-gotchas) | [Related Skills](#related-skills)

## References

- [./references/primitives.md](./references/primitives.md) -- native primitive namespaces and their parts
- [./references/hooks.md](./references/hooks.md) -- state, runtime, tool, and scoped-provider hooks
- [./references/adapters.md](./references/adapters.md) -- local persistence, attachments, and remote thread lists
- [./references/custom-backend.md](./references/custom-backend.md) -- a streaming model adapter and a backend-owned thread list
- [./references/migration.md](./references/migration.md) -- moving a web runtime to a native UI layer

## Quick start

Start from the maintained Expo example:

```sh
npx assistant-ui@latest create --example with-expo my-app
cd my-app
```

Set an endpoint that the app can reach. It must be an absolute URL. A physical device cannot resolve its own localhost to your development server.

```dotenv
EXPO_PUBLIC_CHAT_ENDPOINT_URL="https://api.example.com/api/chat"
```

Start Expo:

```sh
npx expo start
```

## Manual setup

Install the native runtime and its AI SDK v7 peer packages in an existing Expo app:

```sh
npx expo install @assistant-ui/react-native @assistant-ui/ai-sdk ai@^7 @ai-sdk/react@^4
```

Host the model route separately. The native app posts UI messages to that route through `AssistantChatTransport`; the route converts them asynchronously for AI SDK v7 and returns a UI message stream.

```ts
import { openai } from "@ai-sdk/openai";
import { convertToModelMessages, streamText } from "ai";

export async function POST(request: Request) {
  const { messages } = await request.json();
  const result = streamText({
    model: openai("gpt-5.6-luna"),
    messages: await convertToModelMessages(messages),
  });

  return result.toUIMessageStreamResponse();
}
```

Create the runtime in a hook. Keep the endpoint in the public Expo environment so the compiled app can reach it.

```tsx
import {
  AssistantChatTransport,
  useChatRuntime,
} from "@assistant-ui/ai-sdk";

const chatEndpoint = process.env.EXPO_PUBLIC_CHAT_ENDPOINT_URL;

export function useAppRuntime() {
  if (!chatEndpoint) {
    throw new Error("EXPO_PUBLIC_CHAT_ENDPOINT_URL is required");
  }

  return useChatRuntime({
    transport: new AssistantChatTransport({ api: chatEndpoint }),
  });
}
```

`AssistantChatTransport` forwards frontend tool schemas and system messages. When the backend enables frontend tools, consume the request tools with `frontendTools` from `@assistant-ui/ai-sdk`; see [tools](../tools/SKILL.md) for the shared backend contract.

## Native chat composition

Put the runtime under the native `AssistantRuntimeProvider`, then compose the thread and composer from primitives. `ThreadPrimitive.MessagesFlatList` is the current list primitive and scopes each row to the corresponding message.

```tsx
import {
  AssistantRuntimeProvider,
  AuiIf,
  ComposerPrimitive,
  MessagePrimitive,
  ThreadPrimitive,
  useAuiState,
} from "@assistant-ui/react-native";
import { Text, View } from "react-native";
import { useAppRuntime } from "./use-app-runtime";

function MessageRow() {
  const role = useAuiState((s) => s.message.role);

  return (
    <View
      style={{
        alignSelf: role === "user" ? "flex-end" : "flex-start",
        backgroundColor: role === "user" ? "#007aff" : "#f0f0f0",
        borderRadius: 16,
        margin: 8,
        padding: 12,
      }}
    >
      <MessagePrimitive.Content />
    </View>
  );
}

function Composer() {
  return (
    <ComposerPrimitive.Root style={{ flexDirection: "row", gap: 8, padding: 12 }}>
      <ComposerPrimitive.Input
        multiline
        placeholder="Message..."
        style={{ borderWidth: 1, borderRadius: 20, flex: 1, padding: 10 }}
      />
      <ComposerPrimitive.Send>
        <Text>Send</Text>
      </ComposerPrimitive.Send>
    </ComposerPrimitive.Root>
  );
}

function ChatScreen() {
  return (
    <ThreadPrimitive.Root style={{ flex: 1 }}>
      <AuiIf condition={(s) => s.thread.isEmpty}>
        <Text style={{ padding: 16 }}>Send a message to begin.</Text>
      </AuiIf>
      <ThreadPrimitive.MessagesFlatList autoScroll>
        {() => <MessageRow />}
      </ThreadPrimitive.MessagesFlatList>
      <Composer />
    </ThreadPrimitive.Root>
  );
}

export default function App() {
  const runtime = useAppRuntime();

  return (
    <AssistantRuntimeProvider runtime={runtime}>
      <ChatScreen />
    </AssistantRuntimeProvider>
  );
}
```

`MessagePrimitive.Content` defaults text parts to native `Text`, not Markdown. Pass `renderText` or use `MessagePrimitive.Parts` with a React Native Markdown renderer when the model returns Markdown. For native keyboard handling, place the thread in a `KeyboardAvoidingView` and tune it for the platform.

Use `useAuiState` inside primitive scopes for reactive values. Use `useAui()` with no arguments for imperative actions such as `aui.composer.send()` and `aui.thread.cancelRun()`. Keep selectors to primitives or stable references instead of constructing an object or array in the selector.

## Generative toolkits

`Metro` must compile files that start with `"use generative"`. Install `@assistant-ui/metro` and wrap the default config. Expo gets `getDefaultConfig` from `expo/metro-config`; a bare React Native app gets it from `@react-native/metro-config`.

```js
const { getDefaultConfig } = require("expo/metro-config");
const { withAui } = require("@assistant-ui/metro");

module.exports = withAui(getDefaultConfig(__dirname));
```

For a backendless toolkit, pass the `aui` option through the wrapper so frontend and human tool schemas remain uploadable:

```js
module.exports = withAui({
  ...getDefaultConfig(__dirname),
  aui: { backendless: true },
});
```

Write the toolkit against `@assistant-ui/react-native`, including native `View` and `Text` renderers. The `"use generative"` directive makes the compiler infer tool kind from `execute`. The [tools](../tools/SKILL.md) skill covers backend, frontend, human, provider, external, and stub tool semantics.

## Common Gotchas

**The mobile app posts to /api/chat and never reaches the server**

- Native apps have no browser origin for relative requests. Set `EXPO_PUBLIC_CHAT_ENDPOINT_URL` to the complete route URL.
- Use a host reachable from the simulator or physical device. Device localhost is the device itself.

**The provider is mounted but primitives throw or show no state**

- Create the runtime with `useChatRuntime` or another supported runtime hook, then pass it as `runtime={runtime}` to `AssistantRuntimeProvider`.
- Render primitives below that provider. Message, part, attachment, queue, suggestion, and thread-list-item primitives also need their corresponding parent render scope.

**A web Thread or an Elements import does not render in the Expo app**

- The shadcn Elements catalog is DOM and Tailwind based. Build the native UI with `@assistant-ui/react-native` primitives and React Native styles.

**The message list does not stay at the bottom**

- Use `ThreadPrimitive.MessagesFlatList` with `autoScroll` for new screens. `ThreadPrimitive.Messages` is retained for compatibility and defaults its auto-scroll options to false.

**Toolkits compile as ordinary modules or tool schemas never reach the model**

- Add `withAui` to `Metro` before using `"use generative"`.
- Import the identical generative module in the server build. If there is no server build, set `aui: { backendless: true }`.

**Markdown appears as literal asterisks and fences**

- `MessagePrimitive.Content` uses native `Text` by default. Supply a native Markdown renderer through `renderText`.

## Related Skills

- [setup](../setup/SKILL.md) -- `create`, `initialize`, and configure assistant-ui web projects
- [primitives](../primitives/SKILL.md) -- DOM primitives for web applications, not React Native UI
- [runtime](../runtime/SKILL.md) -- runtime behavior and backend transport concepts shared with native
- [tools](../tools/SKILL.md) -- generative toolkits, their backend route, and custom tool UI

© assistant-ui · MIT · assistant-ui/skills

Similar skills

Everything for Mobile →

react-native by other authors comes first.

react-native vercel-labs This skill turns JSON specs into native React Native mobile UIs. Use it when screens need state bindings, conditional visibility, and actions. CX Codex +1 3,200installs react-native jezweb Performance guidelines and architecture patterns for React Native and Expo apps. Helps coding agents handle list virtualization, UI-thread animations, and navigation. CX Codex +1 1,300installs react-native alinaqi A skill that sets project structure, component patterns and testing rules for React Native. Kicks in when the agent touches .tsx files or iOS and Android code. CX Codex +1 566installs setup-react-native-storybook storybookjs Automates Storybook 10 installation in React Native projects. It handles configurations for Expo, Expo Router, plain React Native CLI, and Re.Pack. CX Codex +1 526installs stream-react-native GetStream A skill for building, integrating, migrating and auditing Stream Chat, Video and Feeds in React Native CLI and Expo apps. CX Codex +1 706installs argent-react-native-app-workflow software-mansion Provides step-by-step procedures for building, running, and debugging React Native apps on iOS simulators or Android emulators. Helps resolve Metro issues and build crashes. CX Codex +1 26,400installs vercel-react-native-skills vercel A rules pack for React Native and Expo work: lists, animations, navigation, native modules. Load it so your agent makes mobile changes the same way every time. CX Codex +1 255,200installs react-native-architecture Seth Hobson A skill for Claude Code, Codex and Cursor that hands your agent project layout, navigation, native modules and offline sync patterns for React Native on Expo. CX Codex 15,087installs react-native-brownfield-migration callstackincubator Guides incremental React Native or Expo adoption in native iOS and Android apps using @callstack/react-native-brownfield. CX Codex +1 5,200installs

From the same set13

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor