Skip to content
Agent UI

Live demo

A complete chat surface, assembled.

Provider and model pickers, suggestion chips, a streaming transcript, and an accessible composer — composed from the same primitives documented in the library. Send a message to see the loading, reply, and stop states.

Try a prompt

Start a conversation

Pick a suggestion above or write your own prompt. Replies are mocked so you can see the streaming, stop, and error states.

Streaming state

isStreaming drives the typing indicator, the stop button, and the disabled composer state.

useChat docs →

Provider + model

Controlled pickers keep selection in your state so requests always carry the right model.

ModelPicker docs →

Keyboard first

Enter sends, Shift+Enter breaks, Escape closes menus, and the transcript is a live region.

MessageList docs →

Wire it to your backend

The only integration point is onSend. Return the assistant text, or throw to surface the inline error state.

tsx
const chat = useChat({
onSend: async (text) => {
const res = await fetch("/api/chat", {
method: "POST",
body: JSON.stringify({ text, provider, model }),
});
if (!res.ok) throw new Error("Request failed");
return res.text();
},
});
 
<ChatShell
messages={chat.messages}
isStreaming={chat.isStreaming}
error={chat.error}
onSend={chat.send}
onStop={chat.stop}
/>

What to try

  • Send a suggestion chip and watch the mock reply appear after the loading state.
  • Switch provider or model before sending — the reply echoes your current selection.
  • Press Stop while the mock request is pending to cancel it and mark the reply cancelled.
  • Clear the transcript to return to the empty state with suggestion chips.

Frequently asked questions

Is this chat demo connected to a real AI model?

No. The demo returns a mock echo after a short delay so the streaming and loading states are visible without an API key. Replace the onSend callback passed to useChat with a fetch call to your backend to make it real.

How do I show streaming tokens in the transcript?

useChat exposes isStreaming and keeps the transcript in sync. ChatShell renders the typing indicator and stop button while isStreaming is true. For token-by-token reveal, render StreamingText inside a MessageBubble and drive its content from your SSE or streaming fetch loop.

Can users switch providers and models?

Yes. The toolbar combines ProviderSelector and ModelPicker. Both are controlled components: pass selectedId and onChange, then include the current selection in your request payload.

Does the chat work with a keyboard and screen reader?

Yes. The transcript is a polite live region, the composer supports Enter to send and Shift+Enter for a new line, the stop button is labelled, and every picker supports arrow-key navigation and Escape to close.