Get Started
Install the package, build your app, and run a text request on the device.
You need Expo SDK 54+ and a native build. Expo Go is not supported. For this text example, use an Apple Intelligence device on iOS 26+ or a supported Android device.
Here for speech, images, face detection, or embeddings? Install below, then follow that guide for its configuration and example.
1. Install
npx expo install expo-ai-kitAll features are off by default. Nothing native is compiled into your app until you turn a feature on here. Each option adds one feature and nothing else; this example needs llm:
{
"expo": {
"plugins": [["expo-ai-kit", { "llm": true }]]
}
}In an existing React Native app, install Expo modules first, then set the same option by hand: expoAiKit.llm=true in android/gradle.properties and "expoAiKit.llm": "true" in ios/Podfile.properties.json (or $ExpoAiKitLLM = true in the Podfile). The llm and speech options need Android minSdkVersion 26.
Set the Android minimum SDK in an Expo project
npx expo install expo-build-properties{
"expo": {
"plugins": [["expo-build-properties", { "android": { "minSdkVersion": 26 } }]]
}
}2. Build the app
Plugin options change native code, so run a native build for your platform, or create an EAS development build.
npx expo run:ios --device
# or
npx expo run:android --deviceIn a bare React Native app, run npx pod-install for iOS and rebuild using your normal native workflow.
3. Run a request
Replace App.tsx with this component. In a Router app, use it as a screen. Tap Ask; the answer appears below the button.
import { useState } from 'react';
import { Button, ScrollView, Text } from 'react-native';
import { isAvailable, prepareBuiltInModel, sendMessage } from 'expo-ai-kit';
export default function App() {
const [answer, setAnswer] = useState('Tap Ask to begin.');
const [busy, setBusy] = useState(false);
async function ask() {
setBusy(true);
try {
if (!(await isAvailable())) {
setAnswer('The built-in text model is unavailable on this device.');
return;
}
setAnswer('Preparing the model…');
await prepareBuiltInModel();
setAnswer('Thinking…');
const { text } = await sendMessage([
{ role: 'user', content: 'Explain gravity in one sentence.' },
]);
setAnswer(text);
} catch (error) {
setAnswer(error instanceof Error ? error.message : String(error));
} finally {
setBusy(false);
}
}
return (
<ScrollView contentContainerStyle={{ padding: 24, paddingTop: 80, gap: 16 }}>
<Button title={busy ? 'Working…' : 'Ask'} onPress={ask} disabled={busy} />
<Text selectable>{answer}</Text>
</ScrollView>
);
}Android may download the built-in model during preparation. Subsequent requests reuse the prepared model.
If the device has no built-in text model, use a downloadable model or see troubleshooting. Next, stream responses and add conversation history.