React Native
A streaming XHR adapter (the streaming fetch bare RN lacks), an AsyncStorage-backed StitchStore for persistent sessions, and AppState/NetInfo refetch helpers — plus the re-exported useStitch / useStitchStream hooks.
Use @stitchapi/react-native when you call stitches from a bare React Native app.
The useStitch / useStitchStream hooks are re-exported verbatim from
@stitchapi/react — they are pure useSyncExternalStore
over the shared @stitchapi/query-core
store and run unchanged on React Native. What this package adds is the platform
glue bare RN needs.
On Expo, use @stitchapi/expo instead —
expo/fetch streams natively, so it needs none of the XHR shim or polyfills
below.
Why a dedicated adapter
StitchAPI is streaming-first, but bare RN's global fetch cannot stream:
response.body is undefined, not a ReadableStream
(facebook/react-native#27741).
RN's XMLHttpRequest, however, exposes responseText incrementally as bytes
arrive. rnStreamAdapter wraps that growing text in a real ReadableStream, which
is exactly what core's sse / stream decoders consume
— while non-streaming requests delegate to core's buffered xhrAdapter.
Example
Install the bindings, the shared store, core, and React Native:
npm install @stitchapi/react-native @stitchapi/react @stitchapi/query-core stitchapistitchapi, react, and react-native are peer dependencies;
@react-native-async-storage/async-storage and @react-native-community/netinfo
are optional peers, for the store and reconnect helpers.
Polyfills (streaming only)
Hermes ships no TextEncoder / TextDecoder / ReadableStream, which the stream
decoder needs. Install them once at your app's entry — rnStreamAdapter throws a
precise error if they're missing. Non-streaming stitches need none of this.
import 'react-native-polyfill-globals/auto';Wire it into a seam
rnStreamAdapter streams when asked and buffers otherwise; asyncStorageStore
makes the auth vault and throttle counters survive app restarts:
import AsyncStorage from '@react-native-async-storage/async-storage';
import { asyncStorageStore, rnStreamAdapter } from '@stitchapi/react-native';
import { seam } from 'stitchapi';
export const api = seam({
baseUrl: 'https://api.example.com',
adapter: rnStreamAdapter(),
store: asyncStorageStore(AsyncStorage),
});Stream deltas into a component
import { rnStreamAdapter, useStitchStream } from '@stitchapi/react-native';
import { sse } from 'stitchapi/sse';
const chat = sse({
url: 'https://api.example.com/chat',
adapter: rnStreamAdapter(),
});
function Chat({ prompt }: { prompt: string }) {
const { chunks, isStreaming } = useStitchStream(chat, { body: { prompt } });
return (
<Text>
{(chunks as string[]).join('')}
{isStreaming ? '▌' : null}
</Text>
);
}Refetch on foreground / reconnect
The data lifecycle a mobile app expects — refetch when the app returns to the foreground, and when connectivity comes back:
import NetInfo from '@react-native-community/netinfo';
import {
useAppActiveRefetch,
useReconnectRefetch,
useStitch,
} from '@stitchapi/react-native';
function Inbox() {
const q = useStitch(getInbox, {});
useAppActiveRefetch(q);
useReconnectRefetch(q, { netInfo: NetInfo });
// ...
}useAppActiveRefetch uses RN's built-in AppState (no extra dependency);
useReconnectRefetch takes your NetInfo module. Both are also available as plain
functions — onAppActive(appState, cb) and onReconnect(netInfo, cb) — for use
outside React.
The persistent store
asyncStorageStore(storage, options?) accepts any client matching
{ getItem, setItem, removeItem }. Values ride in a JSON envelope with an absolute
expiry (AsyncStorage has no native TTL), and incr is serialized so concurrent
increments stay atomic — the throttle counter behaves exactly as it does on Redis.
It is proven against the same verifyStoreContract kit every
StitchStore passes.
See also
Angular
injectStitch and injectStitchStream expose a stitch's lifecycle as both Angular signals and an RxJS observable over the framework-agnostic @stitchapi/query-core store — plus an optional TanStack Query adapter.
Expo
expoFetchAdapter streams over expo/fetch with no polyfills, and expoSecureStore keeps auth tokens encrypted — over the same hooks, AsyncStorage store, and lifecycle helpers as @stitchapi/react-native.