Wire up the UI
Create the chat interface using the useChat hook
Wire up the UI
Now that you have an API route that can query an LLM, it's time to setup your frontend. The AI SDK's UI package abstracts the complexity of a chat interface into one hook, useChat.
Update your root page (app/(tabs)/index.tsx) with the following code to show a list of chat messages and provide a user message input:
import { generateAPIUrl } from '@/utils';
import { useChat } from '@ai-sdk/react';
import { DefaultChatTransport } from 'ai';
import { fetch as expoFetch } from 'expo/fetch';
import { useState } from 'react';
import { View, TextInput, ScrollView, Text, SafeAreaView } from 'react-native';
export default function App() {
const [input, setInput] = useState('');
const { messages, error, sendMessage } = useChat({
transport: new DefaultChatTransport({
fetch: expoFetch as unknown as typeof globalThis.fetch,
api: generateAPIUrl('/api/chat'),
}),
onError: error => console.error(error, 'ERROR'),
});
if (error) return <Text>{error.message}</Text>;
return (
<SafeAreaView style={{ height: '100%' }}>
<View
style={{
height: '95%',
display: 'flex',
flexDirection: 'column',
paddingHorizontal: 8,
}}
>
<ScrollView style={{ flex: 1 }}>
{messages.map(m => (
<View key={m.id} style={{ marginVertical: 8 }}>
<View>
<Text style={{ fontWeight: 700 }}>{m.role}</Text>
{m.parts.map((part, i) => {
switch (part.type) {
case 'text':
return <Text key={`${m.id}-${i}`}>{part.text}</Text>;
default:
return null;
}
})}
</View>
</View>
))}
</ScrollView>
<View style={{ marginTop: 8 }}>
<TextInput
style={{ backgroundColor: 'white', padding: 8 }}
placeholder="Say something..."
value={input}
onChange={e => setInput(e.nativeEvent.text)}
onSubmitEditing={() => {
sendMessage({ text: input });
setInput('');
}}
autoFocus={true}
/>
</View>
</View>
</SafeAreaView>
);
}This page utilizes the useChat hook, which will, by default, use the POST API route you created earlier (/api/chat). The hook provides functions and state for handling user input and form submission. The useChat hook provides multiple utility functions and state variables:
messages- the current chat messages (an array of objects withid,role, andpartsproperties).sendMessage- a function to send a message to the chat API.
The component uses local state (useState) to manage the input field value, and handles form submission by calling sendMessage with the input text and then clearing the input field.
The LLM's response is accessed through the message parts array. Each message contains an ordered array of parts that represents everything the model generated in its response. These parts can include plain text, reasoning tokens, and more that you will see later. The parts array preserves the sequence of the model's outputs, allowing you to display or process each component in the order it was generated.
You use the expo/fetch function instead of the native node fetch to enable streaming of chat responses. This requires Expo 52 or higher.
Create the API URL Generator
Because you're using expo/fetch for streaming responses instead of the native fetch function, you'll need an API URL generator to ensure you are using the correct base url and format depending on the client environment (e.g. web or mobile). Create a new file called utils.ts in the root of your project and add the following code:
import Constants from 'expo-constants';
export const generateAPIUrl = (relativePath: string) => {
const origin = Constants.experienceUrl.replace('exp://', 'http://');
const path = relativePath.startsWith('/') ? relativePath : `/${relativePath}`;
if (process.env.NODE_ENV === 'development') {
return origin.concat(path);
}
if (!process.env.EXPO_PUBLIC_API_BASE_URL) {
throw new Error(
'EXPO_PUBLIC_API_BASE_URL environment variable is not defined',
);
}
return process.env.EXPO_PUBLIC_API_BASE_URL.concat(path);
};This utility function handles URL generation for both development and production environments, ensuring your API calls work correctly across different devices and configurations.
Before deploying to production, you must set the EXPO_PUBLIC_API_BASE_URL
environment variable in your production environment. This variable should
point to the base URL of your API server.