A complete guide to integrating Anthropic's Claude API into a React Native or Expo app, from your first API call to streaming responses and connecting Claude to native UI components.
To use the Claude API in a React Native app, you call Anthropic's REST endpoint from a backend proxy, never directly from the client. The Anthropic TypeScript SDK runs in Node.js but not in the React Native JS runtime, so mobile apps make HTTP requests to a lightweight server or Next.js API route that holds your API key, then stream or return Claude's response to the app. Combined with WireAI, Claude can respond with structured JSON that renders directly as a native component instead of a text bubble.
Claude has become the default choice for agentic mobile apps that need long context, nuanced instruction-following, and reliable JSON output. GPT-4o has broader name recognition, but Claude 3.5 Sonnet consistently outperforms on structured output tasks, which matters enormously when your mobile app depends on the model returning valid component props rather than an apologetic paragraph. This guide covers every layer of the integration: server setup, mobile client, streaming, error handling, and connecting Claude to native UI with WireAI.
Why Claude works particularly well for mobile agents
The constraint that defines mobile AI development is not latency, it is reliability. A model that returns valid JSON 95% of the time produces a broken UI 1 in 20 turns. On a desktop web app, a user refreshes. On a phone mid-conversation, they close the app and leave a one-star review.
Claude 3.5 Sonnet and Claude 3 Haiku both excel at the task structure mobile agents need: pick one component from a registry, return its props in a specific JSON shape, and follow the schema even when the user's input is ambiguous. In internal WireAI testing across 10,000 simulated turns, Claude 3.5 Sonnet produced schema-valid JSON output 97.8% of the time without any retries. Claude 3 Haiku reached 96.1% at roughly one-tenth the cost and one-quarter the latency. Both are strong choices depending on your budget-versus-quality trade-off.
Claude also supports a 200K token context window. For a mobile health app that keeps the last 30 conversation turns plus user profile data in context, this means you never need to implement context trimming in early versions, just keep appending.
Architecture: why the API key cannot live in the app
Every mobile app can be decompiled. API keys stored in .env files, React Native config, AsyncStorage, or even MMKV are recoverable by anyone who downloads your binary. Anthropic API keys have no per-request cost cap, a single leaked key can accumulate thousands of dollars in charges in an hour.
The correct architecture has three layers:
- Mobile app, sends user messages to your backend, receives responses. Never touches the Anthropic API directly.
- Backend proxy, holds the
ANTHROPIC_API_KEYin a server environment variable. Validates the request (auth, rate limiting, content policy), calls Claude, returns the result. A Next.js API route on Vercel or Netlify Functions on Netlify works well here. - Anthropic API, only reachable from your server, never from the client.
If you are building a prototype for personal use only, calling Anthropic directly from the app is technically possible, but the moment the app leaves your device you have a security problem.
Setting up the backend proxy
The simplest backend is a Next.js API route. If you are already using Next.js for your marketing site (as WireAI does), you can add this route to the same project and deploy it to Netlify.
// app/api/chat/route.ts
import Anthropic from "@anthropic-ai/sdk";
import { NextRequest, NextResponse } from "next/server";
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
export async function POST(req: NextRequest) {
const { messages, systemPrompt } = await req.json();
// Add your own auth check here before calling Claude
const auth = req.headers.get("Authorization");
if (!auth || auth !== `Bearer ${process.env.APP_SECRET}`) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const response = await client.messages.create({
model: "claude-3-5-sonnet-20241022",
max_tokens: 1024,
system: systemPrompt,
messages,
});
return NextResponse.json({
content: response.content[0].type === "text"
? response.content[0].text
: null,
usage: response.usage,
});
}Store ANTHROPIC_API_KEY in your deployment environment (Netlify environment variables, Vercel env vars, etc.), never commit it to your repository.
Calling Claude from React Native
From the React Native side, this is a regular fetch call to your API route. The Anthropic SDK itself does not need to be installed in the mobile app, only on the server.
// hooks/useClaude.ts
import { useState } from "react";
type Message = { role: "user" | "assistant"; content: string };
export function useClaude(systemPrompt: string) {
const [messages, setMessages] = useState<Message[]>([]);
const [isLoading, setIsLoading] = useState(false);
const sendMessage = async (userText: string) => {
const newMessages: Message[] = [
...messages,
{ role: "user", content: userText },
];
setMessages(newMessages);
setIsLoading(true);
try {
const res = await fetch("https://your-site.netlify.app/api/chat", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.EXPO_PUBLIC_APP_SECRET}`,
},
body: JSON.stringify({ messages: newMessages, systemPrompt }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
setMessages([
...newMessages,
{ role: "assistant", content: data.content },
]);
} catch (err) {
console.error("Claude error:", err);
} finally {
setIsLoading(false);
}
};
return { messages, sendMessage, isLoading };
}Note that EXPO_PUBLIC_APP_SECRET is a shared secret between the app and your server, it is not the Anthropic API key. This adds a basic layer of protection against random internet traffic hitting your proxy. For production apps, replace this with proper user authentication (Supabase Auth, for example) so each request is tied to an authenticated user.
Streaming Claude responses token by token
Non-streaming responses make users wait 2–4 seconds staring at a spinner. Streaming lets you show tokens as they arrive, which feels dramatically faster even if the total time is the same. For a full deep-dive on streaming architecture, see the React Native LLM streaming guide. Here is the server-side change to enable it:
// app/api/chat/stream/route.ts, streaming version
import Anthropic from "@anthropic-ai/sdk";
import { NextRequest } from "next/server";
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
export async function POST(req: NextRequest) {
const { messages, systemPrompt } = await req.json();
const stream = client.messages.stream({
model: "claude-3-5-sonnet-20241022",
max_tokens: 1024,
system: systemPrompt,
messages,
});
const readable = new ReadableStream({
async start(controller) {
for await (const event of stream) {
if (
event.type === "content_block_delta" &&
event.delta.type === "text_delta"
) {
controller.enqueue(
new TextEncoder().encode(event.delta.text)
);
}
}
controller.close();
},
});
return new Response(readable, {
headers: { "Content-Type": "text/event-stream" },
});
}Connecting Claude to native UI with WireAI
Text streaming is fine for a general chat app. But if you want Claude to render an interactive native component, a mood selector, a hydration tracker, a workout card, you need structured JSON output, not a text stream. This is exactly what the WireAI runtime is designed for.
The system prompt that WireAI generates for your component registry tells Claude to respond with a specific JSON shape. When Claude follows the prompt, WireAI validates the output against your Zod schemas and renders the correct React Native component. The integration looks like this:
import { WireAIProvider, useWireAIThread } from "wireai-rn";
import { ClaudeAdapter } from "@wireai/cloud"; // coming in v0.2
// Configure Claude as the WireAI adapter
const adapter = new ClaudeAdapter({
endpoint: "https://your-site.netlify.app/api/chat",
secret: process.env.EXPO_PUBLIC_APP_SECRET,
});
// In your screen component:
export function AgentScreen() {
const { messages, sendMessage } = useWireAIThread();
return (
<WireAIProvider adapter={adapter}>
{messages.map((msg) => (
<WireAIMessageRenderer key={msg.id} message={msg} />
))}
</WireAIProvider>
);
}WireAIMessageRenderer checks whether the message is a plain text response or a structured component response. If it is structured, it renders the native component. If Claude produces invalid JSON (hallucination, edge case), it falls back to a text bubble. No crash, no red screen.
Choosing the right Claude model for your app
Anthropic currently offers three tiers for mobile-relevant use cases:
- Claude 3.5 Sonnet, Best overall intelligence, highest JSON reliability, slowest and most expensive. Use for complex agents where accuracy matters more than cost.
- Claude 3 Haiku, Fastest and cheapest. Response time is close to GPT-4o Mini. JSON reliability is still excellent for structured output tasks. Use for high-frequency interactions like chat history summaries or quick intent detection.
- Claude 3 Opus, Highest reasoning capability, but significantly slower and more expensive than Sonnet. Rarely the right choice for mobile where latency is user-visible.
For most WireAI apps, the recommended starting point is Claude 3.5 Sonnet for generative UI responses (where JSON accuracy matters) and Claude 3 Haiku for lighter tasks like summarizing conversation context or extracting user intent before routing to the component renderer.
Production checklist
- Rate limiting, Add per-user rate limits in your proxy (e.g., 20 requests per minute). Without this, one power user can drive up significant costs.
- Token budgeting, Log
response.usagefor every call. Set up an alert if daily token spend exceeds a threshold. - Context window management, Claude 3.5 Sonnet supports 200K tokens, but billing is per token. Keep the last 15–20 turns in context for chat, not the full history.
- Error handling, Anthropic returns 529 (overloaded) occasionally during peak load. Implement exponential backoff with a max of 3 retries.
- User attribution, Pass a unique user identifier in the
metadatafield of each Anthropic API call. This lets you filter your API usage dashboard by user if you need to investigate abuse.
Ready to build a Claude-powered mobile app? Install the WireAI runtime with npm install wireai-rn zod, follow the 30-minute build guide, and connect Claude via the Webhook adapter while @wireai/cloud ships.