Expo and TanStack Start
Purpose and Scope
This page explains the two non-Next.js starter paths in the AI SDK getting-started flow: Expo for mobile React Native-style applications and TanStack Start for full-stack React applications. Both quickstarts teach the same application shape: a streaming chat user interface backed by a server-side route that calls AI SDK Core. The important idea is that framework setup changes where the route lives and how the framework exposes server handlers, while the model call, message conversion, and UI stream response primitives remain the same. Sources: content/docs/02-getting-started/07-expo.mdx, content/docs/02-getting-started/08-tanstack-start.mdx
Use these guides when you already know you are building in Expo or TanStack Start and want the shortest path to a working streaming agent. The docs describe the app as a “simple agent with a streaming chat user interface,” not as a one-off completion demo. That wording matters because the route receives conversation history, converts UI messages into model messages, streams model output, and returns a UI message stream that the client can consume incrementally. If you are still choosing a provider or framework, read the provider and navigation pages first, then return here for implementation details.
Relevant Source Files
content/docs/02-getting-started/07-expo.mdx— Defines the Expo quickstart, prerequisites, dependency installation,.env.localsetup, Expo route handler path, and AI SDK streaming route example.content/docs/02-getting-started/08-tanstack-start.mdx— Defines the TanStack Start quickstart, prerequisites, dependency installation,.envsetup, TanStack server route handler, and equivalent AI SDK streaming route example.packages/react/README.md— Identifies the React UI package surface used by these starters, includinguseChat,useCompletion, anduseObject.
Core Primitives
Both starters use the ai package as the server-side entry point and @ai-sdk/react as the client-side React integration. The quickstarts install ai, @ai-sdk/react, and zod; the docs describe zod as the schema validation library used for defining tool inputs, even though the minimal route example only streams text from messages. The React package README names useChat, useCompletion, and useObject as the UI hooks, which explains why the getting-started flow installs @ai-sdk/react even when the route handler imports primarily from ai. Sources: content/docs/02-getting-started/07-expo.mdx, content/docs/02-getting-started/08-tanstack-start.mdx, packages/react/README.md
The server primitive is streamText. It accepts a model and model-ready messages, then returns a streaming result whose stream is converted for UI consumption. The message boundary is explicit: clients send UIMessage[], the route calls convertToModelMessages(messages), and the model receives the converted form. The response boundary is also explicit: toUIMessageStream({ stream: result.stream }) adapts the core stream, and createUIMessageStreamResponse packages it as an HTTP response. That sequence is the shared mental model for both Expo and TanStack Start, regardless of framework routing syntax.
Setup Flow
For Expo, start by creating the app with pnpm create expo-app@latest my-ai-app, then change into the project directory. The guide requires Expo 52 or higher and lists Node.js 22+ plus pnpm as prerequisites. After app creation, install the shared dependencies with one of the package-manager commands shown in the docs, for example pnpm add ai @ai-sdk/react zod. The quickstart uses Vercel AI Gateway so the ai package can access many model providers through one gateway key, while still allowing you to switch later by installing a provider-specific package. Sources: content/docs/02-getting-started/07-expo.mdx
For TanStack Start, create the project with pnpm create @tanstack/start@latest my-ai-app, change into the project directory, and install the same ai, @ai-sdk/react, and zod dependencies. The TanStack guide has the same Node.js 22+ and pnpm prerequisite and the same AI Gateway key requirement. The main setup difference is environment file naming: Expo uses .env.local, while TanStack Start uses .env. In both cases the variable is AI_GATEWAY_API_KEY, and the docs state that the AI SDK Vercel AI Gateway provider defaults to that environment variable. Sources: content/docs/02-getting-started/07-expo.mdx, content/docs/02-getting-started/08-tanstack-start.mdx
pnpm create expo-app@latest my-ai-app
cd my-ai-app
pnpm add ai @ai-sdk/react zod
touch .env.localpnpm create @tanstack/start@latest my-ai-app
cd my-ai-app
pnpm add ai @ai-sdk/react zod
touch .envRoute Handler Patterns
The Expo route handler lives at app/api/chat+api.ts. It exports an async POST(req: Request) function, reads { messages } from await req.json(), calls streamText, and returns createUIMessageStreamResponse. The Expo example includes response headers for Content-Type: application/octet-stream and Content-Encoding: none, which are part of the documented route response in that starter. Because the route receives the full UI message history, the model can use prior turns as context instead of treating every request as an isolated prompt. Sources: content/docs/02-getting-started/07-expo.mdx
import {
streamText,
UIMessage,
convertToModelMessages,
createUIMessageStreamResponse,
toUIMessageStream,
} from 'ai';
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
const result = streamText({
model: __MODEL__,
messages: await convertToModelMessages(messages),
});
return createUIMessageStreamResponse({
stream: toUIMessageStream({ stream: result.stream }),
headers: {
'Content-Type': 'application/octet-stream',
'Content-Encoding': 'none',
},
});
}The TanStack Start route handler lives at src/routes/api/chat.ts and uses TanStack Router’s createFileRoute('/api/chat') server handler shape. Inside the POST handler, the AI SDK logic is intentionally the same: read UIMessage[], convert them to model messages, stream with streamText, and return createUIMessageStreamResponse. This separation is useful when moving between frameworks. You should expect routing, file names, and request objects to follow the host framework, but the AI SDK boundary remains a compact, provider-agnostic stream pipeline. Sources: content/docs/02-getting-started/08-tanstack-start.mdx
import {
streamText,
UIMessage,
convertToModelMessages,
createUIMessageStreamResponse,
toUIMessageStream,
} from 'ai';
import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/api/chat')({
server: {
handlers: {
POST: async ({ request }) => {
const { messages }: { messages: UIMessage[] } = await request.json();
const result = streamText({
model: __MODEL__,
messages: await convertToModelMessages(messages),
});
return createUIMessageStreamResponse({
stream: toUIMessageStream({ stream: result.stream }),
});
},
},
},
});Provider and Model Choice
Both quickstarts use Vercel AI Gateway as the first provider path. The docs emphasize that the Gateway provider ships with the ai package and can access hundreds of models from different providers with one API key. That choice reduces first-run setup because readers do not need to install a separate provider package immediately. It also keeps the route example focused on the AI SDK flow rather than on provider-specific authentication, imports, and model naming details. Sources: content/docs/02-getting-started/07-expo.mdx, content/docs/02-getting-started/08-tanstack-start.mdx
Gateway is not a lock-in mechanism in these guides. The quickstart notes explicitly say you can switch to any provider or model by installing its provider package and checking the available AI SDK providers. In practice, keep the surrounding route structure and replace the provider import plus model: __MODEL__ placeholder with the model expression for your chosen provider. The rest of the code should still read UI messages, call convertToModelMessages, stream with streamText, and return a UI message stream response.
System-to-Code Mapping
| Concern | Expo starter | TanStack Start starter | Shared AI SDK concept |
|---|---|---|---|
| App creation | pnpm create expo-app@latest my-ai-app | pnpm create @tanstack/start@latest my-ai-app | Framework scaffold before adding AI SDK dependencies |
| Dependency install | ai @ai-sdk/react zod | ai @ai-sdk/react zod | Core generation, React hooks, and schemas |
| Environment file | .env.local | .env | AI_GATEWAY_API_KEY for Gateway authentication |
| Route path | app/api/chat+api.ts | src/routes/api/chat.ts | Server endpoint that accepts chat messages |
| Message type | UIMessage[] | UIMessage[] | UI-facing conversation history |
| Stream response | createUIMessageStreamResponse with Expo headers | createUIMessageStreamResponse | UI message stream returned over HTTP |
This mapping is the best way to reason about the two starters. Treat the framework column as the code you adapt to your project, and treat the shared AI SDK column as the contract you preserve. When your app becomes more capable, you can add tools, schemas, provider options, or different hooks, but the first working chat route should remain small and inspectable. The React README’s hook list also points to likely next UI expansions: use useChat for chat, useCompletion for single text completion experiences, and useObject for structured object generation. Sources: packages/react/README.md
Next Steps
After the route returns a stream, wire the client to the React hook that matches your product shape. For a chatbot, start with useChat from @ai-sdk/react, because these quickstarts are centered on UIMessage history and streaming chat responses. If you need a simpler text box that completes a prompt, evaluate useCompletion; if the UI expects validated structured data, evaluate useObject. From there, read the streaming, tools, and provider pages so you understand how message history, tool schemas, model selection, and provider-specific options fit into the same request lifecycle.
Related pages: choosing-a-provider, streaming-foundations, ui-overview-and-chatbot, generating-text-and-streaming.