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.local setup, 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, .env setup, 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, including useChat, useCompletion, and useObject.

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.local
pnpm create @tanstack/start@latest my-ai-app
cd my-ai-app
pnpm add ai @ai-sdk/react zod
touch .env

Route 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

ConcernExpo starterTanStack Start starterShared AI SDK concept
App creationpnpm create expo-app@latest my-ai-apppnpm create @tanstack/start@latest my-ai-appFramework scaffold before adding AI SDK dependencies
Dependency installai @ai-sdk/react zodai @ai-sdk/react zodCore generation, React hooks, and schemas
Environment file.env.local.envAI_GATEWAY_API_KEY for Gateway authentication
Route pathapp/api/chat+api.tssrc/routes/api/chat.tsServer endpoint that accepts chat messages
Message typeUIMessage[]UIMessage[]UI-facing conversation history
Stream responsecreateUIMessageStreamResponse with Expo headerscreateUIMessageStreamResponseUI 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.