> ## Documentation Index
> Fetch the complete documentation index at: https://docs.polpo.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# React Native

> Build mobile agent apps with React Native and the Polpo SDK.

The HTTP and chat APIs in `@polpo-ai/sdk` use `fetch` and work on React Native versions with streaming `ReadableStream` support. React Native does not provide a portable native `EventSource`; use a polyfill for project events.

## Install

```bash theme={null}
npm install @polpo-ai/sdk
```

For SSE support, install an EventSource polyfill:

```bash theme={null}
npm install react-native-sse
```

## Setup

```typescript theme={null}
// lib/polpo.ts
import { PolpoClient } from "@polpo-ai/sdk";

export const client = new PolpoClient({
  baseUrl: "https://api.your-app.example/polpo",
});
```

<Warning>Do not embed an `sk_live_` or `sk_test_` project key in a mobile binary. Route requests through your authenticated backend and inject the key there.</Warning>

## Chat screen

```tsx theme={null}
import { useState, useCallback } from "react";
import { View, TextInput, FlatList, Text, TouchableOpacity } from "react-native";
import { client } from "../lib/polpo";

interface Message {
  role: "user" | "assistant";
  content: string;
}

export function ChatScreen() {
  const [messages, setMessages] = useState<Message[]>([]);
  const [input, setInput] = useState("");
  const [streaming, setStreaming] = useState(false);

  const send = useCallback(async () => {
    if (!input.trim() || streaming) return;
    const userMsg: Message = { role: "user", content: input };
    const updated = [...messages, userMsg];
    setMessages(updated);
    setInput("");
    setStreaming(true);

    try {
      const stream = client.chatCompletionsStream({
        agent: "assistant",
        messages: updated,
      });

      let content = "";
      setMessages(prev => [...prev, { role: "assistant", content: "" }]);

      for await (const chunk of stream) {
        const delta = chunk.choices[0]?.delta?.content ?? "";
        content += delta;
        setMessages(prev => [
          ...prev.slice(0, -1),
          { role: "assistant", content },
        ]);
      }
    } catch (err) {
      setMessages(prev => [
        ...prev,
        { role: "assistant", content: "Error: " + (err as Error).message },
      ]);
    } finally {
      setStreaming(false);
    }
  }, [messages, input, streaming]);

  return (
    <View style={{ flex: 1, padding: 16 }}>
      <FlatList
        data={messages}
        keyExtractor={(_, i) => String(i)}
        renderItem={({ item }) => (
          <View style={{ marginBottom: 12 }}>
            <Text style={{ fontWeight: "bold" }}>
              {item.role === "user" ? "You" : "Agent"}
            </Text>
            <Text>{item.content}</Text>
          </View>
        )}
      />
      <View style={{ flexDirection: "row", gap: 8 }}>
        <TextInput
          value={input}
          onChangeText={setInput}
          placeholder="Message..."
          style={{ flex: 1, borderWidth: 1, borderColor: "#ccc", padding: 8, borderRadius: 8 }}
          onSubmitEditing={send}
        />
        <TouchableOpacity onPress={send} disabled={streaming}>
          <Text>Send</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
}
```

## Agent list

```tsx theme={null}
import { useEffect, useState } from "react";
import { View, Text, FlatList, Image } from "react-native";
import { client } from "../lib/polpo";

export function AgentsScreen() {
  const [agents, setAgents] = useState([]);

  useEffect(() => {
    client.getAgents().then(setAgents);
  }, []);

  return (
    <FlatList
      data={agents}
      keyExtractor={(a) => a.name}
      renderItem={({ item }) => (
        <View style={{ flexDirection: "row", padding: 16, gap: 12 }}>
          {item.identity?.avatar && (
            <Image
              source={{ uri: item.identity.avatar }}
              style={{ width: 40, height: 40, borderRadius: 20 }}
            />
          )}
          <View>
            <Text style={{ fontWeight: "bold" }}>
              {item.identity?.displayName ?? item.name}
            </Text>
            <Text style={{ color: "#666" }}>{item.role}</Text>
          </View>
        </View>
      )}
    />
  );
}
```

## SSE events

For real-time updates, use `react-native-sse`:

```typescript theme={null}
import EventSource from "react-native-sse";

const url = "https://api.your-app.example/polpo/events?filter=task:,agent:";
const es = new EventSource(url, {
  headers: { Authorization: `Bearer ${appSessionToken}` },
});

es.addEventListener("task:transition", (event) => {
  const data = JSON.parse(event.data);
  console.log("Task moved to:", data.to);
});

// Cleanup
es.close();
```

## Notes

* The `@polpo-ai/react` hooks package uses `useSyncExternalStore` which works on React Native 0.69+
* For Expo, all APIs work without extra native modules
* File uploads (agent avatars, storage) use standard `fetch` with `FormData`
* Chat streaming uses the Fetch API's `ReadableStream` — supported in React Native 0.71+ (Hermes)

<Tip>
  For older React Native versions without `ReadableStream` support, use the non-streaming `client.chatCompletions()` method instead.
</Tip>
