Fluxio
← Retour au blog

Build a Typing Indicator From the Token Stream

Le prix des tokensEN

A useful AI typing indicator should reflect the request lifecycle, not run on an unrelated timer. In this walkthrough, you will connect waiting dots, streamed text, and completion feedback to one event-driven state machine. The goal is simple: show what the application knows without pretending to know what the model is thinking.

§1 Define the Typing Indicator States

Start with six explicit states: idle, waiting, streaming, complete, cancelled, and error. Keep the accumulated message separate from the status so a failed request does not erase text already received. Assign every generation a unique request identifier.

  • – Show waiting dots immediately after the user submits a message.
  • – Replace waiting dots with a trailing cursor when visible text arrives.
  • – Remove all activity indicators when the request reaches a terminal state.

For this implementation, derive indicator visibility from the state rather than maintaining a separate boolean. That gives you one place to decide whether the interface should look active.

§2 Connect a Streaming Backend Through Fluxio

Put a backend route between the browser and the model service. Have that route authenticate the application user, validate the message, and initiate the upstream streaming request. Keep the service credential on the server rather than embedding it in browser code.

Fluxio fits this architecture as a streaming-first, pay-per-token AI API relay. Use its supplied API base, https://api.fluxioapi.com, with /v1 appended for OpenAI-style clients. Keep provider-specific parsing in the backend adapter so the browser only handles your application’s events.

Define a small event contract containing text_delta, completed, and failed events, each associated with the request identifier. Treat those names as your own application protocol, not assumed Fluxio response fields. Emit completed only after the adapter recognizes successful upstream completion.

§3 Parse Stream Events Before Updating the Indicator

If your backend uses server-sent events, return the text/event-stream content type and encode messages as UTF-8. A blank line terminates an SSE event; an arbitrary network chunk does not. Follow the event format rather than treating each read as a complete message. ([html.spec.whatwg.org](https://html.spec.whatwg.org/multipage/server-sent-events.html))

For a fetch-based browser client, decode the response body incrementally with TextDecoderStream. Then feed decoded text into an SSE parser. These are separate jobs: decoding produces text, while parsing identifies complete events. TextDecoderStream supports streaming byte-to-text conversion. ([developer.mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoderStream?utm_source=openai))

A parser such as eventsource-parser accepts partial or complete input chunks and emits parsed events. Use it instead of splitting every received chunk on newline characters and hoping the boundaries align. ([github.com](https://github.com/rexxars/eventsource-parser))

§4 Drive Typing Feedback From Visible Text Deltas

When a text_delta arrives, first check its request identifier against the active generation. Ignore stale events. For a nonempty text fragment, append its exact contents and transition from waiting to streaming. Do not trim fragments: spaces belong to the answer.

In your adapter, keep metadata, usage information, and tool activity separate from visible answer text. Let those events update a dedicated status if needed, but do not use them to announce that an answer has started appearing.

Buffer incoming fragments and schedule at most one pending requestAnimationFrame callback to update the displayed message. That API schedules work before a repaint. Flush pending text explicitly on completion rather than depending on a future animation frame, especially in background tabs. ([developer.mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame?trk=article-ssr-frontend-pulse_little-text-block&utm_source=openai))

§5 Handle Completion, Cancellation, and Quiet Streams

On completed, flush pending text, clear timers, and set the state to complete. If the transport closes without your protocol’s completion event, preserve the partial answer and mark it interrupted rather than silently declaring success.

Connect a Stop action to an AbortController passed into fetch. Aborting can cancel the request and response-body consumption. Also propagate cancellation through your backend to the upstream request where supported. ([developer.mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/AbortController?utm_source=openai))

Choose a configurable inactivity threshold, such as ten seconds, as a product setting. After that interval without visible progress, show “Still waiting for a response.” Do not interpret silence alone as completion or invent a percentage.

§6 Test Typing Feedback Under Stream Failures

Test split events, split multibyte characters, delayed first text, rapid cancellation, stale events, and disconnection after partial output. Confirm that each terminal path removes the indicator and preserves the appropriate message.

Use a persistent status region with role set to status for concise lifecycle announcements, rather than announcing every fragment. That role provides polite live-region behavior for assistive technology. ([w3.org](https://www.w3.org/WAI/WCAG22/Techniques/aria/ARIA22.html?utm_source=openai))

Sign up for Fluxio to build your streaming chat experience.

Créer un compte Fluxio →

Plus d'articles du courant