e30e75b5d4
Changesets / Create Version PR (push) Has been cancelled
Deploy Shadcn Registry / Deploy Production (push) Has been cancelled
Template Metrics / LOC + Bundle Size (push) Has been cancelled
Code Quality / Oxlint + Oxfmt (push) Has been cancelled
Code Quality / Template Sync (push) Has been cancelled
Code Quality / Build Changed Packages (push) Has been cancelled
Code Quality / Test Changed Packages (push) Has been cancelled
Deploy Expo Example / Deploy Production (push) Has been cancelled
Deploy Ink Example / Deploy Production (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.12) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Has been cancelled
169 lines
4.9 KiB
Plaintext
169 lines
4.9 KiB
Plaintext
---
|
|
title: Expo React Native AI Chat
|
|
description: Native iOS and Android AI chat app with Expo — drawer navigation, thread persistence, and the assistant-ui React Native components for mobile.
|
|
---
|
|
|
|
## Overview
|
|
|
|
A full-featured React Native chat application built with Expo and `@assistant-ui/react-native`. It demonstrates drawer-based thread management, streaming OpenAI responses, and a polished native UI with dark mode support.
|
|
|
|
## Features
|
|
|
|
- **Native UI**: Built with React Native primitives — no web views
|
|
- **Drawer Navigation**: Swipeable sidebar for thread management
|
|
- **Thread Management**: Create, switch, and browse conversations
|
|
- **Streaming**: Real-time streaming via a server-side chat API endpoint
|
|
- **Dark Mode**: Automatic light/dark theme support
|
|
- **Keyboard Handling**: Proper `KeyboardAvoidingView` integration
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# Clone the repo
|
|
git clone https://github.com/assistant-ui/assistant-ui.git
|
|
cd assistant-ui
|
|
|
|
# Install dependencies
|
|
pnpm install
|
|
|
|
# Set your API key
|
|
echo 'OPENAI_API_KEY="sk-..."' > examples/with-expo/.env
|
|
|
|
# Run the example
|
|
pnpm --filter with-expo start
|
|
```
|
|
|
|
## Code
|
|
|
|
### Runtime Setup
|
|
|
|
The runtime uses `useChatRuntime` from `@assistant-ui/react-ai-sdk` with an `AssistantChatTransport` that connects to a chat API endpoint:
|
|
|
|
```tsx title="hooks/use-app-runtime.ts"
|
|
import { useMemo } from "react";
|
|
import {
|
|
useChatRuntime,
|
|
AssistantChatTransport,
|
|
} from "@assistant-ui/react-ai-sdk";
|
|
|
|
const CHAT_API = process.env.EXPO_PUBLIC_CHAT_ENDPOINT_URL ?? "/api/chat";
|
|
|
|
export function useAppRuntime() {
|
|
const transport = useMemo(
|
|
() => new AssistantChatTransport({ api: CHAT_API }),
|
|
[],
|
|
);
|
|
return useChatRuntime({ transport });
|
|
}
|
|
```
|
|
|
|
### App Layout
|
|
|
|
`AssistantRuntimeProvider` wraps the Expo Router drawer layout inside a `GestureHandlerRootView`. The layout also handles font loading, theming, a "New Chat" button in the header, and tool registration via `useAui`:
|
|
|
|
```tsx title="app/_layout.tsx"
|
|
import {
|
|
DarkTheme,
|
|
DefaultTheme,
|
|
ThemeProvider,
|
|
} from "@react-navigation/native";
|
|
import { Drawer } from "expo-router/drawer";
|
|
import { StatusBar } from "expo-status-bar";
|
|
import "react-native-reanimated";
|
|
import { Pressable, useColorScheme } from "react-native";
|
|
import { Ionicons } from "@expo/vector-icons";
|
|
import { useFonts } from "expo-font";
|
|
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
|
|
|
import {
|
|
AssistantRuntimeProvider,
|
|
useAui,
|
|
Tools,
|
|
} from "@assistant-ui/react-native";
|
|
import { useAppRuntime } from "@/hooks/use-app-runtime";
|
|
import { ThreadListDrawer } from "@/components/thread-list/ThreadListDrawer";
|
|
import { expoToolkit } from "@/components/assistant-ui/tools";
|
|
|
|
function NewChatButton() {
|
|
const aui = useAui();
|
|
const colorScheme = useColorScheme();
|
|
const isDark = colorScheme === "dark";
|
|
|
|
return (
|
|
<Pressable
|
|
onPress={() => {
|
|
aui.threads().switchToNewThread();
|
|
}}
|
|
style={{ marginRight: 16 }}
|
|
>
|
|
<Ionicons
|
|
name="create-outline"
|
|
size={24}
|
|
color={isDark ? "#ffffff" : "#000000"}
|
|
/>
|
|
</Pressable>
|
|
);
|
|
}
|
|
|
|
function DrawerLayout() {
|
|
const colorScheme = useColorScheme();
|
|
|
|
return (
|
|
<ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
|
|
<Drawer
|
|
drawerContent={(props) => <ThreadListDrawer {...props} />}
|
|
screenOptions={{
|
|
headerRight: () => <NewChatButton />,
|
|
drawerType: "front",
|
|
swipeEnabled: true,
|
|
drawerStyle: { backgroundColor: "transparent" },
|
|
}}
|
|
>
|
|
<Drawer.Screen name="index" options={{ title: "Chat" }} />
|
|
</Drawer>
|
|
<StatusBar style="auto" />
|
|
</ThemeProvider>
|
|
);
|
|
}
|
|
|
|
export default function RootLayout() {
|
|
const [fontsLoaded] = useFonts(Ionicons.font);
|
|
const runtime = useAppRuntime();
|
|
const aui = useAui({
|
|
tools: Tools({ toolkit: expoToolkit }),
|
|
});
|
|
|
|
if (!fontsLoaded) return null;
|
|
|
|
return (
|
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
<AssistantRuntimeProvider runtime={runtime} aui={aui}>
|
|
<DrawerLayout />
|
|
</AssistantRuntimeProvider>
|
|
</GestureHandlerRootView>
|
|
);
|
|
}
|
|
```
|
|
|
|
```tsx title="app/index.tsx"
|
|
import { Thread } from "@/components/assistant-ui/thread";
|
|
|
|
export default function ChatPage() {
|
|
return <Thread />;
|
|
}
|
|
```
|
|
|
|
### Key Architecture
|
|
|
|
| Layer | Purpose |
|
|
|-------|---------|
|
|
| `useChatRuntime` | Creates a runtime backed by an `AssistantChatTransport` connecting to a chat API endpoint |
|
|
| `AssistantRuntimeProvider` | Provides the runtime to the entire app (thread and composer scopes are set up automatically) |
|
|
| `useAuiState((s) => s.thread)` | Reactive thread state access with selector for fine-grained re-renders |
|
|
| `useAuiState((s) => s.composer)` | Reactive composer state access |
|
|
| Primitives | `ThreadMessages`, `ComposerInput`, `MessageContent`, etc. |
|
|
|
|
## Source
|
|
|
|
<SourceLink href="https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-expo" />
|