Streaming text Pro
Model output, paced. Tokens arrive in bursts and stalls, and the text doesn't show it: it reveals at a steady rhythm, speeding up only to drain a backlog, and each new piece surfaces out of a soft blur instead of popping in. A caret breathes at the end while more is coming.
Why do springs feel better than easing curves?
Pro previewInstallation
npx @neelshha/ui@latest login
npx @neelshha/ui@latest add streaming-textUsage
import { StreamingText } from "@/components/ui/streaming-text";
<StreamingText streaming={isStreaming}>{text}</StreamingText>Pass the whole text received so far, not each chunk. If a new value doesn't continue the last one, like a regenerated answer, it starts over from the part the two share. Text that's there on mount, like an answer from history, shows at once.
With the AI SDK
const { messages, status } = useChat();
messages.map((message, i) => (
<StreamingText
key={message.id}
streaming={status === "streaming" && i === messages.length - 1}
>
{message.parts
.map((part) => (part.type === "text" ? part.text : ""))
.join("")}
</StreamingText>
));Source
API
Other props go to the div. It sets aria-busy while text is still coming, and wraps white space like pre-wrap. With reduced motion, text shows as it arrives, without the fade.
| Prop | Type | Default | Description |
|---|---|---|---|
children | string | Everything received so far. Append to it as chunks arrive. | |
streaming | boolean | false | True while more text is on its way. Keeps the caret on. |
speed | number | 90 | The steady pace, in characters per second. A backlog drains faster, so the text never trails far behind what has arrived. |
onDone | () => void | Fires once a finished stream has been revealed in full. |