A demo Expo/React Native app showing how to integrate @tambo-ai/react on mobile. It's a simple activity logger where each Tambo thread is a conversation-style log entry.
Expo 55 / React Native 0.83 / React 19.
npm installCreate a .env file:
EXPO_PUBLIC_TAMBO_API_KEY=your_key_here
Run:
npx expo startStandard TamboProvider wrapping the app, with tools and initial messages passed in. A random userKey is generated on first launch and persisted with AsyncStorage.
Since @tambo-ai/react's built-in components render HTML elements, this app renders everything with React Native primitives (View, Text, Pressable, FlatList).
The message renderer (components/log-entry.tsx) switches on the content block type:
text— chat bubble (View+Text)tool_use— interactive UI (e.g.QuickAnswerwith tappablePressablebuttons)component— dynamically rendered Tambo components (e.g.StarRating,SummaryCard) viaComponentRendererresource— inline image via React NativeImagetool_result— skipped (rendered inline with the correspondingtool_use)
The chat screen (app/log/[id].tsx) uses an inverted FlatList with KeyboardAvoidingView.
useTamboThreadListpowers the log list screenuseTamboprovidesmessages,startNewThread, andswitchThreadfor the chat screenuseTamboThreadInputhandles the input bar state and submission
Two TamboComponent entries are registered via the components prop on TamboProvider:
StarRating— interactive 1-5 star input; usesuseTamboComponentStateto sync the selected rating to the backendSummaryCard— display-only card with key-value pairs for end-of-conversation recaps
The AI decides when to render each component based on the conversation context.
The app defines a ask_multiple_choice tool whose executor returns a Promise that blocks until the user taps an answer. The SDK's tool executor awaits tool functions, so a pending Promise simply pauses execution. Meanwhile, the tool_use content block streams in and the message renderer shows tappable buttons. When the user taps one, resolvePrompt() resolves the Promise and the conversation continues.
The SDK targets web browsers. Several things are needed to make it work in React Native's JavaScript runtime.
Imported at the top of app/_layout.tsx before any SDK code loads:
| API | Why it's needed | Polyfill |
|---|---|---|
crypto.randomUUID() / getRandomValues() |
SDK generates IDs throughout | expo-crypto |
Array.prototype.toSorted() |
SDK's event accumulator uses this ES2023 method; not available in default Hermes | [...arr].sort() shim |
Event / EventTarget |
The eventsource package (SSE streaming) extends these |
Minimal class polyfills |
fetch with streaming body |
SDK reads SSE response as a stream; React Native's built-in fetch doesn't support ReadableStream on the response body |
expo/fetch replaces globalThis.fetch |
All polyfills use runtime guards (if (!...)) so they become no-ops if the runtime already provides the API. If you opt in to Hermes v1 (available in Expo 55), the toSorted and Event/EventTarget polyfills will be skipped automatically.
Two web-only dependencies are stubbed out so Metro doesn't try to bundle them:
react-dom— peer dependency of the SDK, unused on mobilereact-media-recorder— web-only direct dependency of the SDK
config.resolver.resolveRequest = (context, moduleName, platform) => {
if (moduleName === "react-dom" || moduleName === "react-media-recorder") {
return { type: "empty" };
}
// ...
};The SDK lists react-dom as a peer dependency, which pulls in a version incompatible with the React version Expo pins. Since we don't use react-dom at all, overrides prevent the resolution conflict:
"overrides": {
"react-dom": "$react",
"@types/react-dom": { "@types/react": "$@types/react" }
}app/
_layout.tsx TamboProvider + Stack navigator
index.tsx Log list (thread list)
log/[id].tsx Chat screen (single log)
components/
log-entry.tsx Message content renderer
quick-answer.tsx Multiple-choice button UI
star-rating.tsx Star rating input (useTamboComponentState)
summary-card.tsx Structured key-value summary card
input-bar.tsx Text input + send
lib/
polyfills.ts Web API polyfills for React Native runtime
tools.ts Tool definitions + resolvePrompt()
components.ts TamboComponent registrations
system-prompt.ts System prompt and initial messages
metro.config.js Stubs for web-only dependencies