UX patterns for AI features: streaming and graceful failure

UX patterns for AI features from a real in-app assistant: paced streaming, agent status, record cards, confirm-before-create and graceful failure.

By Hamza Ghouri

Most AI features fail on UX before they fail on model quality. The answer might be good, but the user stared at a frozen screen for eight seconds, could not tell what the assistant was doing, and had no way to act on the result without copying text into another screen. Good UX patterns for AI features fix all three.

One assistant I built recently is the in-app AI assistant in the Recond Flutter app, for a multi-tenant vehicle-reconditioning platform. It was built spec-first, with numbered specs and amendments, and most of the patterns below come straight from it. The rest is general guidance I'd apply to any AI feature.

Stream the reply, and pace it

Streaming is the single biggest perceived-speed win. Users see the first words within a second and read while the rest arrives. The server side is a standard streaming endpoint, which I cover in streaming LLM responses with Node.js and Server-Sent Events.

Raw streaming has a problem, though: tokens arrive in uneven bursts. Three words, a pause, a whole sentence at once. It looks jittery. In the Recond assistant the streamed reply is paced so it reads as typing. The idea is simple: buffer what arrives and release it at a steady rate, speeding up when the buffer grows so pacing never adds real delay.

export function pacedWriter(render: (text: string) => void, tickMs = 16) {
  let shown = "";
  let pending = "";
  let ended = false;

  const timer = setInterval(() => {
    if (pending.length > 0) {
      // drain faster when far behind, so the reply never lags the stream for long
      const n = Math.max(2, Math.ceil(pending.length / 20));
      shown += pending.slice(0, n);
      pending = pending.slice(n);
      render(shown);
    } else if (ended) {
      clearInterval(timer);
    }
  }, tickMs);

  return {
    push(chunk: string) { pending += chunk; },
    end() { ended = true; },
  };
}

Feed it chunks from your stream reader, call end() when the stream closes, and the user gets a smooth, typing-like reply.

Show what the agent is doing

An assistant that calls tools spends time on things that produce no text: looking up a record, checking a status, querying a list. A blank pause during that time feels like a hang.

The Recond assistant shows what the agent is doing while it works. A short status line in the product's own words (for example, "looking up the vehicle") turns waiting into progress. A few rules make it work:

  • Describe the step in the user's language, using names from the product, never internal tool names.
  • Replace the status as steps change, so the screen does not fill with log lines.
  • Remove it the moment the reply starts, so it never competes with the answer.

Answer with cards that open the record

When the answer is about something in the product, text alone is the weakest format. The user has to read it, then go find the thing.

The Recond assistant renders record detail, state and list cards, each with an Open button that jumps straight to the record. Ask about a vehicle and you get a card with its key fields and a way into it. Ask for a list and you get a list card. A few details matter:

  • Fill cards from real data. For any card UI, I'd let the model choose which card to show and pull the card's fields from the database, so the numbers are always correct.
  • Show a loader while a card renders. Cards often need a fetch after the model has spoken. The Recond assistant shows a loader while a card renders, so the user knows something is coming.
  • Keep the text short around a card. One sentence of context beats a paragraph repeating what the card already shows.

Confirm before the assistant changes anything

Reading data is low risk. Creating or changing records is where an assistant needs a human checkpoint.

When the Recond agent proposes creating a record, it shows a create card that the user confirms or cancels. Nothing is created until the user confirms. And a cancelled card stays cancelled, so nothing later in the conversation can quietly revive it. That last rule sounds small, but it is what makes users trust the confirm button. I write more about where people should stay in control in human-in-the-loop AI design.

Help the user ask the next question

A blank composer is intimidating. Many users do not know what an assistant can do.

  • Follow-up suggestion chips. The Recond assistant shows follow-up chips above the composer. One tap sends a relevant next question, which teaches people the assistant's range without a tutorial.
  • Timestamps on messages. Conversations about work get revisited. Knowing when something was said matters when the underlying records change.
  • Chat search. Once people use an assistant daily, they want to find that answer from last week. Search is cheap to build and worth adding early.

Fail gracefully

Every AI feature fails sometimes: timeouts, rate limits, a model that misreads the question. The UX question is what the user sees.

  • Say what happened in plain words and offer a retry. Never leave a spinner running forever; put a timeout on every request.
  • Keep partial output. If the stream breaks halfway, keep what arrived and mark it incomplete.
  • Keep the input. A failed send should never eat the user's message.
  • Move long work out of the chat. Anything that takes minutes should become a background job with a status the user can come back to, as described in running slow AI jobs in the background with queues.
  • Admit uncertainty. When the assistant cannot find a record, say so and suggest a next step. A confident wrong card is worse than "I couldn't find that vehicle".

The short version

Stream replies and pace them so they read as typing. Show what the agent is doing while it works. Answer with cards that open the real record, show a loader while they render, and require a confirm card before anything is created. Add follow-up chips, timestamps and search, and treat every failure state as a screen you design on purpose.

Want this kind of work on your product?

Get in touch