AI Message Stream
A streaming message list — character-by-character typing fade-in, Markdown surfacing progressively, with built-in stop / retry animations and auto-scroll.
This is a WebberUI Pro component
Free during the launch campaign: sign up or sign in, then hit “Copy install command” in the preview above and it installs straight away — no payment, no credit card. The command below returns 401 while you are signed out.
npx shadcn@latest add "https://webberui.com/r/ai-message-stream.json?t=<install token>"Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<AiMessageStream />
Installation
npx shadcn@latest add "https://webberui.com/r/ai-message-stream.json?t=<install token>"Or, once registries are configured in components.json, install it as @webberui/ai-message-stream.
Usage
import {
AIMessageStream,
type StreamMessage,
} from "@/components/ui/ai-message-stream";
const messages: StreamMessage[] = [
{ id: "u1", role: "user", content: "Tell me about streaming replies", status: "done" },
{ id: "a1", role: "assistant", content: "This is a **streaming** reply…", status: "streaming" },
];
<AIMessageStream
messages={messages}
streaming={isStreaming}
onStop={() => controller.abort()}
onRetry={regenerate}
/>Just bind streaming to the model's streaming state: while true, the last assistant message surfaces character by character at charsPerSecond and a "Stop generating" button appears in the header row; once it flips back to false, "Regenerate" is shown instead. Assistant message content surfaces as lightweight Markdown — bold, italics, inline code, lists, headings, and links take shape the moment their syntax completes.
With a real stream, just keep merging newly received tokens into that message's content and the reveal animation catches up to the current length automatically; giving content all at once (as in the example above) types out the whole thing.
Props
AIMessageStream
| Prop | Type | Default | Description |
|---|---|---|---|
messages | StreamMessage[] | — | Array of messages |
streaming | boolean | false | When true, the last assistant message is revealed character by character |
onStop | () => void | — | Callback for clicking "Stop"; providing it shows the stop button while streaming |
onRetry | () => void | — | Callback for clicking "Retry"; providing it shows the retry button while not streaming |
charsPerSecond | number | 45 | Typing reveal speed (characters / second) |
markdown | boolean | true | Whether assistant messages are parsed as Markdown |
autoScroll | boolean | true | Scrolls to the bottom automatically as content grows (paused while manually scrolled up) |
maxHeight | number | string | 320 | Maximum height of the scroll container |
className | string | — | Appended to the outer container's className |
StreamMessage
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier, used as the key and as the boundary for the character-by-character reveal |
role | "user" | "assistant" | Role: the user is right-aligned, the assistant left-aligned |
content | string | The content; assistant messages surface as Markdown by default |
status | "streaming" | "done" | "stopped" | "error" | Status; error / stopped show the matching notice |
How it works
- The character-by-character reveal advances via
requestAnimationFrameatcharsPerSecond, catching up to the current length automatically as the content grows - Each word surfaces on mount with a fade in, an upward shift, and a de-blur, and Markdown blocks take shape as their syntax closes
- An unclosed code block (mid-stream) still renders live as
code, matching how a real stream looks - A blinking cursor is shown while streaming, and a three-dot waiting indicator appears while there is no content yet
- "Stop / Retry" swap in and out with
AnimatePresence, and the stop button carries a pulse - Content growth auto-scrolls while pinned to the bottom, pauses when the user scrolls up, and resumes on returning to the bottom
- Every
requestAnimationFrameand timer is cleaned up on state changes and on unmount
Accessibility
- The scroll container carries
role="log"andaria-live="polite", so streamed content is announced by assistive technology - Message bubbles carry
aria-busywhile streaming, which is cleared once done - Links carry
rel="noopener noreferrer"and open in a new tab - When the user has "reduce motion" enabled at the system level: the character-by-character reveal is skipped in favor of showing the complete content, the cursor and per-word animations are removed, and the waiting indicator and stop pulse degrade to static
AI Prompt Input Pro
An advanced AI chat input integrating attachments, a model menu, a voice waveform, slash commands, @ mentions, and a streaming stop state.
AI Reasoning Panel
A collapsible panel for an AI's thinking process — expanding automatically while thinking, with a shimmering title and step-by-step reveal, then totalling the elapsed time and optionally collapsing itself.