Stream LLM Responses in React: A Practical Tutorial
Streaming an LLM response makes a chat interface feel faster because users see text as it arrives instead of waiting for a complete answer. This tutorial walks through a practical React setup using an OpenAI-compatible streaming API, including request handling, incremental rendering, cancellation, and error states.
1. Keep your API key on the server
Do not put a provider key in React code or a VITE_ or REACT_APP_ environment variable. Anything bundled for the browser can be inspected. Create a small backend route that accepts the user’s message, adds your API key, and forwards the request to your model provider. The browser should call your own route, such as /api/chat.
Your server can use an OpenAI-compatible SDK configured with the API base URL and a secret stored in the server environment. For 59API, the base URL is https://api.59api.com. Confirm the model name and endpoint format in your account documentation, then pass a chat-completions request with streaming enabled.
2. Request a streamed response
On the server, send the conversation and set stream: true. Return the provider’s stream to the browser without buffering it. Make sure the route supports POST requests, forwards the request’s abort signal where possible, and handles provider errors before writing response headers. Set appropriate streaming headers for your server framework, and disable proxy buffering if your deployment platform enables it.
A simplified browser request looks like this:
const response = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages }) });
Check response.ok before reading the body. If it is false, show a useful error rather than leaving the chat waiting indefinitely. Also check that response.body exists; streaming fetch requires a readable response body in the browser.
3. Read and display chunks in React
Use the response body’s reader to process incoming bytes, and a TextDecoder to convert them into text. The stream may split a single event across several chunks, so keep an unfinished buffer between reads. If your backend emits Server-Sent Events (SSE), split complete events on blank lines, read each data: line, and parse its JSON payload. Append each content delta to the current assistant message.
Keep the partial answer in React state. A typical pattern is to create a new assistant message when the request begins, then update that message as deltas arrive. Avoid rebuilding the entire conversation from scratch for every token; update only the streaming message so the UI stays predictable. When the stream signals completion, stop reading and mark the message as finished.
Do not assume every event contains visible text. OpenAI-compatible streams can include role information, empty deltas, finish reasons, or a final usage event. Parse the provider’s event format deliberately, and treat a stream-ending marker such as [DONE] as completion if the service uses it.
4. Add loading, cancellation, and error states
Track whether a response is in progress. Disable duplicate submits while streaming, but keep the stop control available. Create an AbortController for each request, pass its signal to fetch, and call abort() when the user stops generation or leaves the conversation. The backend should propagate cancellation upstream when its framework and provider support it.
Wrap reading and parsing in try/catch. Distinguish a user-initiated abort from a network or server error. Preserve any text already received if generation fails, and show a concise status so users know the response is incomplete. Clear the loading state in a finally block.
5. Test the real behavior
Test with a short prompt, a long response, a slow connection, an invalid model name, and a stopped generation. Confirm that text appears before completion, line breaks render correctly, and repeated submissions do not mix output between messages. Also test through your production proxy: local development may stream correctly while a proxy buffers the full response.
Choose a model endpoint that fits your budget
Streaming is a client experience pattern; it does not require a particular model vendor. 59API offers pay-as-you-go access to Claude Opus, Sonnet, Haiku, Fable, and GPT models through an OpenAI-SDK-compatible relay, with https://api.59api.com as its API base URL. Its low-cost pricing can help control experimentation costs, while access to native, official-quality models avoids relying on downgraded substitutes. Referral rebates are also available. Review current model availability and pricing, then sign up for 59API if a flexible, lower-cost relay fits your application.