Deep dive on AI UX design for frontend interviews: uncertainty handling, streaming states, trust and safety cues, retries, cancellation, and robust interaction patterns for LLM-powered features.
AI UX in Frontend Apps: handling latency, streaming, trust, and failures
Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.
Definition (above the fold)
AI UX is hard because model output is probabilistic, latency varies, and failures are common. A high-quality frontend makes this uncertainty visible and controllable: users must understand what is happening now, what could go wrong, and what they can do next.
Core product model
Treat AI responses as a state machine, not a single API call: idle -> submitting -> streaming -> done | error | canceled. Separate canonical message state from transient UI state (typing indicator, partial tokens, retry banner) to avoid race conditions and duplicate output.
AI UX challenge | Design pattern | Success metric |
|---|---|---|
Variable latency | Immediate optimistic shell + progress state | Lower abandonment before first token |
Partial streaming output | Render incremental draft with clear streaming label | Higher perceived responsiveness |
Model uncertainty | Confidence/citation cues and editable output | Fewer blind copy-paste errors |
Failure and timeout | Actionable error messages + retry/resume controls | Higher recovery completion rate |
Prompt mistakes | Input validation and suggested fixes before submit | Reduced invalid requests |
Unsafe output risk | Output filtering + user reporting actions | Lower trust/safety incident rate |
Runnable example #1: reducer for AI response lifecycle
function aiReducer(state, action) {
switch (action.type) {
case 'submit':
return { ...state, status: 'submitting', text: '', error: null };
case 'stream/start':
return { ...state, status: 'streaming' };
case 'stream/chunk':
return state.status === 'streaming'
? { ...state, text: state.text + action.chunk }
: state;
case 'stream/done':
return { ...state, status: 'done' };
case 'stream/error':
return { ...state, status: 'error', error: action.message };
case 'stream/cancel':
return { ...state, status: 'canceled' };
default:
return state;
}
}
This explicit lifecycle prevents stale updates and gives the UI deterministic rendering rules for each state.
Runnable example #2: cancellable streaming request with safe finalize
const controller = new AbortController();
async function runPrompt(prompt) {
dispatch({ type: 'submit' });
try {
const res = await fetch('/api/ai/stream', {
method: 'POST',
body: JSON.stringify({ prompt }),
signal: controller.signal
});
dispatch({ type: 'stream/start' });
const reader = res.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
dispatch({ type: 'stream/chunk', chunk: decoder.decode(value, { stream: true }) });
}
dispatch({ type: 'stream/done' });
} catch (err) {
if (controller.signal.aborted) dispatch({ type: 'stream/cancel' });
else dispatch({ type: 'stream/error', message: 'Generation failed. Please retry.' });
}
}
Must-have control | User value | Implementation note |
|---|---|---|
Stop generation | Prevents wasted time and token cost | AbortController + explicit canceled state |
Regenerate | Fast retry for weak answer quality | Reuse last prompt + new request ID |
Edit and resubmit | Lets user correct ambiguous prompts | Keep editable prompt history |
Copy with formatting | Safe output reuse | Strip unsafe markup before clipboard write |
Report issue | Feedback loop for bad answers | Attach prompt, response, and error metadata |
Common pitfalls
- Showing a spinner with no progress signal during long generation.
- Merging stream chunks without request IDs, causing mixed outputs from parallel prompts.
- Using vague error messages (for example, "Something went wrong") without recovery actions.
- Presenting generated text as guaranteed fact without confidence or source cues.
When to use / when not to use
Use streaming + rich controls when response time is variable and output quality may require iteration. For deterministic workflows (for example, fixed schema extraction), simplify the UX and prioritize clear validation over conversational UI patterns.
Interview follow-ups
Q1: How do you build trust when model answers can be wrong? A: Add confidence/source cues, easy correction flow, and transparent error handling.
Q2: How do you prevent stale streaming from previous prompts? A: Track request IDs and ignore chunks that do not match the active request.
Q3: Which metric would you track first? A: Time-to-first-token, stream completion rate, and retry success rate.
Implementation checklist / takeaway
Model explicit AI states, stream progressively, expose user controls, and design for failure as a first-class path. Strong AI UX answers combine reliability, clarity, and trust rather than only visual polish.