Fluxio
← Back to the blog

Building a Chat Interface With Live Token Streams

Build notesEN

§1 Step 1: Define the lifecycle of a live reply

A chat interface feels alive when it responds immediately, reveals progress honestly, and stays controllable. Token streaming helps, but the experience depends on everything around those tokens: loading states, scrolling, cancellation, and recovery.

Start with explicit states: idle, submitting, waiting for output, streaming, completed, stopped, and failed. Each state should have a visible consequence. Show the submitted message immediately, create an assistant placeholder, and display a quiet waiting indicator until actual text arrives. Never animate invented words to disguise latency.

§2 Step 2: Route the token stream through your backend

Send the browser request to your own server rather than exposing an API key in client code. Your server authenticates the user, applies usage limits, starts the upstream request, and forwards incremental output. Keep credentials and provider-specific handling behind that boundary.

Fluxio fits this architecture when you want a streaming-first, pay-per-token AI API relay with one key for multiple models. It works with OpenAI SDK clients using https://api.fluxioapi.com/v1 as the base URL. Choose an available model, such as Claude Haiku 4.5, and enable streaming. Confirm the exact model identifier in the service documentation rather than guessing it from the display name.

§3 Step 3: Parse SSE without assuming chunk boundaries

For a browser request that includes a message body, use fetch and consume the response stream. When your server returns server-sent events, parse the SSE framing explicitly. A network chunk is not necessarily one event, one token, or one complete character.

  • – Decode incoming bytes incrementally so multibyte characters survive chunk boundaries.
  • – Keep an unfinished text buffer and extract complete SSE events as delimiters arrive.
  • – Handle content deltas, completion signals, and error events as separate event types.
  • – Preserve the unfinished buffer between reads instead of discarding partial data.

Also disable response buffering where your hosting stack permits it. Test the deployed route, not just localhost: a proxy that batches output can make a correctly implemented stream look like a conventional delayed response.

§4 Step 4: Render readable progress instead of visual noise

Append incoming text to a message buffer, then schedule interface updates at a controlled cadence, such as once per animation frame. Rendering on every tiny delta can cause unnecessary work and make the interface stutter, especially during longer answers.

Keep the assistant message stable as it grows. Avoid repeatedly replacing the entire conversation tree. If you support formatted output, use a renderer that tolerates unfinished syntax and sanitizes generated HTML. Partial formatting should never break the layout or introduce executable content.

Follow the reader, not just the stream. Automatically scroll only while the reader remains near the bottom. If they scroll upward, preserve their position and show a button indicating that new content is arriving below.

§5 Step 5: Make stopping and recovery predictable

Display a stop control while generation is active. Connect it to browser request cancellation, and have your backend propagate cancellation upstream when supported. Preserve the text already received and mark the message as stopped rather than complete.

If the connection fails, retain the partial answer and offer an explicit retry. Do not silently restart generation: a second response may differ and may incur additional usage. Use a request identifier to prevent duplicate submissions, and ignore late events from superseded requests.

§6 Step 6: Test perceived speed and accessible updates

Measure submission feedback time, time to first visible text, gaps between updates, and completion time. Track failures and cancellations alongside latency. These measurements reveal whether delays originate in your application, upstream generation, or delivery infrastructure.

For accessibility, announce status changes through a polite live region rather than announcing every token. Keep keyboard focus stable and respect reduced-motion preferences. Test slow connections, interrupted streams, long responses, and readers who scroll away mid-answer. A chat interface feels alive when it remains understandable under pressure, not merely when its text moves.

Sign up for Fluxio to build your first streaming chat.

Create a Fluxio account →

More from the current