diff --git a/README.md b/README.md index e657665..34946f4 100644 --- a/README.md +++ b/README.md @@ -79,9 +79,43 @@ Just add the ChatKit component, give it a client token, and customize the chat e }, }); - return ; - } - ``` + return ; + } + ``` + +### React Native (Expo) preview + +Experimental support for Expo and bare React Native apps lives in the `@openai/chatkit-react-native` package. The library ships +streaming HTTP helpers, WebRTC wrappers, voice primitives, and opinionated UI building blocks (`ChatList`, `ChatComposer`). + +- Install the package alongside the recommended polyfills: + + ```bash + pnpm add @openai/chatkit-react-native react-native-polyfill-globals react-native-url-polyfill react-native-get-random-values base-64 + ``` + +- Import the polyfills at the top of your app entry point before rendering any ChatKit component: + + ```ts + import 'react-native-polyfill-globals/auto'; + import 'react-native-url-polyfill/auto'; + import 'react-native-get-random-values'; + import 'base-64'; + ``` + +Read the [React Native getting started guide](docs/react-native/getting-started.md) for details on Expo networking, WebRTC +signalling, voice support, and testing recommendations. + +### Testing the React Native package + +Run the package build to ensure the TypeScript sources compile before publishing: + +```bash +pnpm --filter @openai/chatkit-react-native build +``` + +The command bundles the entry points with `tsup` and validates the generated type declarations. If you are iterating on the +package locally, you can append `--watch` to rebuild on file changes. ## License diff --git a/docs/react-native/getting-started.md b/docs/react-native/getting-started.md new file mode 100644 index 0000000..f3a5bda --- /dev/null +++ b/docs/react-native/getting-started.md @@ -0,0 +1,122 @@ +# React Native (Expo) Support + +This guide captures the minimum setup for adopting `@openai/chatkit-react-native` inside a new or existing React Native project. The instructions assume you are using [Expo](https://docs.expo.dev) with the development build workflow, but also call out fallbacks for bare React Native. + +## 1. Install dependencies + +```bash +pnpm add @openai/chatkit-react-native +pnpm add react-native-polyfill-globals react-native-url-polyfill react-native-get-random-values base-64 +pnpm add expo expo-dev-client expo-av expo-speech expo-file-system expo-document-picker +pnpm add react-native-webrtc +# For bare React Native (non-Expo) projects +pnpm add react-native-fetch-api +``` + +## 2. Configure polyfills + +Add the following imports at the top of your app entry (for example `app/index.tsx` when using Expo Router or `index.js` for bare projects): + +```ts +import 'react-native-polyfill-globals/auto'; +import 'react-native-url-polyfill/auto'; +import 'react-native-get-random-values'; +import 'base-64'; +``` + +Polyfills must run before any ChatKit code executes to ensure `TextEncoder`, `TextDecoder`, `ReadableStream`, `URL`, `crypto.getRandomValues`, `atob` and `btoa` are defined. + +## 3. Streaming HTTP helpers + +`createStreamingFetcher` wraps Expo's `fetch` implementation and falls back to `react-native-fetch-api` for apps not running inside the Expo runtime. It exposes lifecycle hooks that map cleanly onto ChatKit's expectations. + +```ts +import { createStreamingFetcher } from '@openai/chatkit-react-native'; + +const streamingFetch = createStreamingFetcher(); + +const { response } = await streamingFetch( + 'https://api.openai.com/v1/responses', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${process.env.EXPO_PUBLIC_OPENAI_API_KEY}`, + }, + body: JSON.stringify({ + model: 'gpt-4.1-mini', + input: [{ role: 'user', content: 'Hello!' }], + }), + }, + { + onResponseStart: () => console.log('stream started'), + onChunk: (chunk) => console.log('chunk', chunk), + onResponseEnd: () => console.log('stream ended'), + }, +); +``` + +## 4. Realtime via WebRTC + +`createWebRTCSession` bridges [`react-native-webrtc`](https://github.com/react-native-webrtc/react-native-webrtc) with ChatKit's realtime API. Provide a signalling adapter that exchanges offers/answers with your backend. The backend should authenticate with OpenAI and create a WebRTC session on behalf of the device. + +```ts +const session = await createWebRTCSession({ + signalling: { + negotiate: async (offer) => { + const response = await fetch('https://your-server.example.com/negotiate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ offer }), + }); + if (!response.ok) throw new Error('Failed to negotiate'); + return response.json(); + }, + }, + onResponseChunk: (chunk) => console.log('Realtime chunk', chunk), +}); + +await session.start(); +``` + +For server-to-server integrations or for environments where WebRTC is not available, `createWebSocketSession` provides a lightweight wrapper over the standard `WebSocket` implementation. + +## 5. Voice mode + +The `useVoiceSession` hook layers [`expo-av`](https://docs.expo.dev/versions/latest/sdk/av/) for microphone capture with [`expo-speech`](https://docs.expo.dev/versions/latest/sdk/speech/) for text-to-speech output. + +```tsx +const voice = useVoiceSession(); + +return ( + +