Loading...
Loading...
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`.
npx skill4agent add assistant-ui/skills react-native@assistant-ui/react-nativeViewTextPressableTextInputFlatList@assistant-ui/ai-sdknpx assistant-ui@latest create --example with-expo my-app
cd my-appEXPO_PUBLIC_CHAT_ENDPOINT_URL="https://api.example.com/api/chat"npx expo startnpx expo install @assistant-ui/react-native @assistant-ui/ai-sdk ai@^7 @ai-sdk/react@^4AssistantChatTransportimport { 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();
}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 }),
});
}AssistantChatTransportfrontendTools@assistant-ui/ai-sdkAssistantRuntimeProviderThreadPrimitive.MessagesFlatListimport {
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.ContentTextrenderTextMessagePrimitive.PartsKeyboardAvoidingViewuseAuiStateuseAui()aui.composer.send()aui.thread.cancelRun()Metro"use generative"@assistant-ui/metrogetDefaultConfigexpo/metro-config@react-native/metro-configconst { getDefaultConfig } = require("expo/metro-config");
const { withAui } = require("@assistant-ui/metro");
module.exports = withAui(getDefaultConfig(__dirname));auimodule.exports = withAui({
...getDefaultConfig(__dirname),
aui: { backendless: true },
});@assistant-ui/react-nativeViewText"use generative"executeEXPO_PUBLIC_CHAT_ENDPOINT_URLuseChatRuntimeruntime={runtime}AssistantRuntimeProvider@assistant-ui/react-nativeThreadPrimitive.MessagesFlatListautoScrollThreadPrimitive.MessageswithAuiMetro"use generative"aui: { backendless: true }MessagePrimitive.ContentTextrenderTextcreateinitialize