Let status drive the UI

Spinner, Stop and Send each follow useChat's status.

AI SDK docsai-sdk.dev
submitted: The message has been sent to the API and we're awaiting the start of the response stream.
AI SDK UI · ChatbotRead docs ↗

From ai-sdk and the Chatbot docs.

One boolean or four states

Press Send, then press a pane's Send again fast.

isLoading booleanBad
Requests 0Duplicates 0
const isLoading = status !== 'ready'
statusGood
Requests 0Duplicates 0
disabled={status !== 'ready'}
'use client';
import { useChat } from '@ai-sdk/react';

export function Chat() {
  const { messages, sendMessage, status, stop } = useChat();
  const busy = status === 'submitted' || status === 'streaming';

  return (
    <>
      <Messages messages={messages} />
      {status === 'submitted' && <Spinner />}
      {busy && <button onClick={() => stop()}>Stop</button>}
      <button
        disabled={status !== 'ready'}
        onClick={() => sendMessage({ text: 'Summarize this thread.' })}
      >
        Send
      </button>
    </>
  );
}

Why it matters, pros and cons

Why it matters

Waiting and streaming need different UI. One boolean spins over text that is already arriving.

Pros

  • The spinner shows only before the first token.
  • Stop is there exactly while a stream can be stopped.
  • No duplicate sends while a reply is in flight.

Cons

  • Four states mean four cases to design.
  • error needs its own UI (next pages).
  • Blocking Send stops queued follow-ups too.