Responsive AI Loading States

Keep the UI responsive during slow AI calls with optimistic updates, transitions, and clear status states.

TL;DR

  1. Show the user's message instantly with an optimistic update.
  2. Track a clear status: idle, loading, streaming, or error.
  3. Keep input snappy by wrapping heavy work in useTransition.

Show It Optimistically

    Add Immediately

    Push the user's message before the call.

    setMessages((m) => [...m, userMsg]);
    useOptimistic

    React 19 tracks the pending message.

    const [opt, addOpt] = useOptimistic(messages);
    Typing Indicator

    Show a placeholder while waiting.

    {pending && <TypingDots />}

Track a Status

    Status Value

    Model the request as one status value.

    type Status = 'idle' | 'loading' | 'error';
    Drive the UI

    Render from the status, not booleans.

    {status === 'loading' && <Skeleton />}
    One Source

    Avoid several loose isLoading flags.

    // one status beats many booleans

Keep Input Snappy

    useTransition

    Mark heavy updates as non-urgent.

    const [isPending, startTransition] =
      useTransition();
    Wrap the Work

    Keep typing instant during updates.

    startTransition(() => setResults(next));
    Show Pending

    Dim the list while it catches up.

    <List className={isPending ? 'dim' : ''} />

Skeletons and Suspense

    Match the Shape

    A skeleton mimics the answer layout.

    <div className="h-4 w-full animate-pulse" />
    Suspense Fallback

    Show a fallback while data loads.

    <Suspense fallback={<Skeleton />}>
    Disable Send

    Block resend until the call finishes.

    <button disabled={status === 'loading'}>

Tips

  1. Render the user's message and a typing indicator before the request resolves, so the app feels instant while the model thinks.
  2. Use React 19's useOptimistic or a single status value to drive the UI instead of juggling several loose isLoading booleans.

Warnings

  1. Do not leave the send button active during a request; a double-click fires two paid calls and can create a raceCondition.
  2. Avoid a bare spinner for multi-second waits; a skeleton or streaming text reads as far faster to users than a lone spinner.

In Practice

FAQ