
I’ve used a lot of AI features over the last year that felt broken. Most of the time, the model was fine. The UI around it wasn’t.
A button that does nothing for six seconds. A spinner that never ends because the stream silently died. A half-written answer that disappears when you click away. A “Regenerate” that wipes out the answer you actually liked.
None of these are AI problems. They’re frontend problems, and they’re very fixable. Here are the patterns I reach for every time I build an AI feature.
Start with a state machine
Normal API calls have three states: loading, success, error. AI requests have more, and if you model them as a pile of booleans (isLoading, isStreaming, hasError, wasStopped), you will end up in impossible combinations like “loading and errored at the same time”.

A discriminated union makes the impossible states unrepresentable:
type AiRequest =
| { status: 'idle' }
| { status: 'submitting'; startedAt: number }
| { status:... premium content.