Fluxio
← Back to the blog

Stream Live AI Replies into a React Chat Component

SSE & APIsEN

A streaming React chat component displays an answer as text arrives, so users can start reading before generation finishes. The implementation has three parts: a React interface, a backend route that protects your API key, and a streamed response connecting them. This walkthrough uses a POST request with a server-sent events response to deliver incremental text.

§1 Step 1: Configure the AI relay on your backend

Keep provider credentials in server environment variables. The browser should call your own /api/chat route, and that route should authenticate with the AI service. Never place the API key in a React environment variable that becomes part of the browser bundle.

Fluxio fits this setup as a streaming-first, pay-per-token AI API relay with one key for multiple model providers. Its API base is https://api.fluxioapi.com; configure OpenAI-style clients with https://api.fluxioapi.com/v1. You can use the OpenAI SDK on your backend and select an available model, such as Claude Haiku 4.5 or Gemini 2.5 Flash, using its exact API identifier from the service.

§2 Step 2: Define React messages and request state

Store messages as objects containing an id, role, and content. Give every message a stable identifier so incoming text updates the correct assistant bubble. Track the input value, request status, and any error separately.

When the user submits a message, capture the conversation you will send, append the user message, and add an empty assistant message. Build the request from that captured conversation, excluding the empty placeholder. Disable submission while the request is active to keep this first implementation predictable.

Use a ref to hold the active AbortController. This lets the Stop button cancel the request without making cancellation depend on a later React render.

§3 Step 3: Forward incremental text through /api/chat

Have your backend validate the submitted messages and request a chat completion with stream: true. With an OpenAI-compatible chat completion stream, iterate over the returned chunks and read incremental text from chunk.choices[0]?.delta?.content. Skip chunks that contain no text.

Return a streaming response with Content-Type set to text/event-stream and Cache-Control set to no-cache. Encode each text fragment as a JSON payload in an SSE data field, followed by two newline characters. For example, a payload can contain a type of delta and a text property.

JSON encoding preserves quotation marks and line breaks inside generated text. Send an explicit done event after successful completion. If generation fails after response headers have been sent, emit a structured error event instead of attempting to change the HTTP status.

§4 Step 4: Read SSE frames with fetch in React

Submit the conversation with fetch, using POST, a JSON body, and the AbortController signal. Check response.ok before reading response.body. Native EventSource does not provide the POST body needed for this request, so parse the response stream yourself.

Read bytes through response.body.getReader() and decode them with TextDecoder using its streaming option. Maintain a text buffer between reads: network chunks can split a character, an SSE frame, or several frames at arbitrary positions.

Extract complete frames at blank-line boundaries, supporting both LF and CRLF line endings. Within each frame, collect its data fields, join them according to SSE rules, and parse the resulting JSON. Keep incomplete data buffered until the next read, then flush the decoder when the stream ends.

§5 Step 5: Append deltas to the active assistant bubble

For each delta event, use a functional state update: setMessages(previous => ...). Find the assistant message by its stable id and append the received text to its existing content. This avoids stale state when several fragments arrive close together.

Render the content as ordinary React text and use white-space: pre-wrap to preserve formatting. Show a generating indicator while the request runs. For very frequent updates, collect text in a ref and commit it once per animation frame to reduce rendering work.

§6 Step 6: Test cancellation and interrupted token streams

Connect Stop to controller.abort(), preserve the partial answer, and mark it as stopped. On the server, propagate client disconnection to the upstream request using your framework's cancellation mechanism. Also abort pending work when the component unmounts.

Test slow connections, malformed events, upstream errors, and disconnections. Treat a stream that closes without a done event as interrupted. Verify that your deployment supports streaming and that proxies do not buffer the entire response before delivery.

Sign up for Fluxio to build your streaming React chat.

Create a Fluxio account →

More from the current