RORK LABJP
TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the roundTOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round
Articles/Dev Tools
Dev Tools/2026-04-13Advanced

LLM Streaming in Rork Apps: Building ChatGPT-Style Real-Time AI Responses with Expo and SSE

A complete guide to implementing LLM streaming (SSE) in React Native and Expo apps. Covers Anthropic and OpenAI streaming APIs, AbortController cancellation, error retry, Cloudflare Workers proxy, and multi-provider abstraction — with production-ready code throughout.

streaming2SSELLM3AI30Anthropic4OpenAI5React Native209Expo149Cloudflare Workers24Rork515

Premium Article

Building an AI chat feature in your Rork app and watching users stare at a loading spinner for ten seconds before the entire response appears at once — there's a specific kind of frustration that comes with that. It's not just a UX problem; it's the difference between an app that feels alive and one that feels broken.

LLM streaming, powered by Server-Sent Events (SSE), solves this at the source. Instead of waiting for the model to finish generating, tokens stream to the client as they're produced. Users see text flowing within milliseconds, and that psychological shift — from "waiting" to "watching it think" — has a measurable impact on retention.

The catch is that streaming in React Native requires a fundamentally different approach than the browser. EventSource doesn't exist in the RN runtime. Reading a ReadableStream from fetch requires specific handling. Wiring up AbortController correctly is trickier than it looks. This guide covers all of it, with production-tested code you can drop into your Rork project.

Why Streaming Is a UX Inflection Point

Before diving into implementation, it's worth understanding what's actually happening from a user perspective.

With blocking (non-streaming) LLM responses, the user experience follows a pattern: send a message, wait through silence, receive a wall of text. For short answers, this is tolerable. For the longer, more nuanced responses that make LLMs valuable, the wait time can push 15–20 seconds with models like Claude Sonnet. Data from mobile chat apps consistently shows 35–45% drop-off during this wait, even when users initiated the request.

Streaming collapses this experience. The first token arrives in under a second. The user's cognitive state shifts from passive waiting to active reading. Even if the total generation time is identical, the perceived speed is dramatically different.

There's also a practical cost angle. When you implement streaming with proper cancellation, users who get enough of an answer midway can stop generation. That cuts API costs on incomplete requests. Depending on your use case, this can reduce monthly LLM spend by 30–50%.

The Core Challenge: React Native Has No EventSource

If you've implemented SSE in a web app, your first instinct might be to reach for EventSource:

// Works in browsers — fails silently in React Native
const es = new EventSource('https://api.anthropic.com/v1/messages');
es.onmessage = (e) => console.log(e.data);

In React Native, this throws or does nothing depending on your setup. The EventSource Web API simply isn't part of the RN runtime.

The correct approach uses fetch() with ReadableStream, which has been available in React Native since Expo SDK 49:

// ✅ Correct approach for React Native / Expo
const response = await fetch('https://api.anthropic.com/v1/messages', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'anthropic-version': '2023-06-01',
    'x-api-key': 'YOUR_API_KEY', // Use a proxy in production — more on this below
  },
  body: JSON.stringify({
    model: 'claude-haiku-4-5-20251001',
    max_tokens: 1024,
    messages: [{ role: 'user', content: 'Hello' }],
    stream: true,
  }),
});
 
const reader = response.body?.getReader();
const decoder = new TextDecoder();
 
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  const chunk = decoder.decode(value, { stream: true });
  // Parse SSE events from chunk
}

If response.body comes back as null, your project is on Expo SDK 48 or earlier. Upgrade to SDK 49+ or install the react-native-fetch-api polyfill. New Rork projects use the latest SDK by default, so this usually isn't an issue.

Parsing SSE Format Correctly

The SSE format that Anthropic sends looks like this:

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":", world"}}

event: message_stop
data: {"type":"message_stop"}

OpenAI uses a slightly different format:

data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","choices":[{"delta":{"content":"Hello"},"index":0}]}

data: [DONE]

A critical detail: one reader.read() call can return multiple SSE events, and a single event can span across multiple reads. If you parse each chunk directly without buffering, you'll see intermittent JSON parse errors in production that are maddeningly hard to reproduce.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Developers stuck on SSE fetch implementation in React Native can now run LLM streaming in production starting today
You'll get copy-paste-ready code that covers AbortController cancellation, error retries, and API cost optimization as a complete set
You'll learn a multi-provider abstraction pattern covering Anthropic, OpenAI, and Gemini — enabling provider switching at runtime without a single line of app code change
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-07-15
The Comma That Fell to the Start of a Line: Rork, Quote Cards, and Japanese Line Breaking
React Native's Text component does not guarantee Japanese line-breaking rules. Here are the violation rates I measured, a WORD JOINER implementation that survives copy and VoiceOver, an onTextLayout audit harness, and how Rork Max (SwiftUI) differs.
Dev Tools2026-07-10
Adding React Compiler to Expo Let Me Delete 41 Hand-Written memo Calls
I enabled React Compiler on Rork-generated React Native screens and measured the rerender counts with Profiler. Here is how I decided which memo and useCallback calls were safe to delete, how to find the components the compiler bailed out on, and how to catch regressions in CI.
Dev Tools2026-07-07
The App Icon Badge Still Says 3 — Rebuilding Expo Badge Counts Around a Single Source of Truth
Why an Expo app's icon badge drifts out of sync with real unread counts and refuses to clear — and how to rebuild it around a single source of truth, with working recompute-and-sync code and the production pitfalls that bite you.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →