Streaming Text
Text revealed as it arrives, with a caret while it is still coming. Driven by elapsed time rather than a per-character interval, so it never drifts or dumps a backlog after a background tab.
src/components/ui/streaming-text.tsxAbout
From the component's own source, where it was written.
Text revealed as it arrives, with a caret while it is still coming.
Reveal is driven by elapsed time rather than a per-character interval. A setInterval per character drifts under load and queues up a backlog when the tab is inactive, so a message that streamed while you were away finishes by dumping a thousand characters at once. Reading the clock each frame makes the reveal correct at any frame rate.
Under prefers-reduced-motion the text appears complete immediately. The animation carries no information the text does not, so there is nothing to lose by skipping it.
The output is a live region: a screen reader should hear the answer arrive, not sit in silence until it finishes.
Install
npx astralyx-ui add streaming-text
Usage
import { StreamingText } from '@/components/ui/streaming-text'
<StreamingText text={answer} speed={240} onDone={scrollToBottom} />Composer
Change the props and the source below follows.
<StreamingText
text={answer}
speed={240}
streaming={true}
caret={true}
/>Examples
Streaming and complete
<StreamingText text={answer} />
<StreamingText text={answer} streaming={false} />API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
StreamingText
Text revealed as it arrives, with a caret while it is still coming.
Extends Omit<ComponentProps<'span'>, 'children'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| text | string | required | The full string. Restarting the reveal is a matter of changing the key. |
| speed | number | 240 | Characters per second, measured against the clock rather than accumulated per frame. |
| streaming | boolean | true | False renders the whole string at once — for replaying a finished message. |
| caret | boolean | true | — |
| onDone | () => void | — | Fires once when the reveal completes. Deliberately not a dependency of the animation, so an inline arrow cannot restart it. |
Behaviour
| Topic | Detail | Description |
|---|---|---|
| reduced motion | automatic | Appears complete immediately under prefers-reduced-motion — the animation carries no information the text does not. |
| accessibility | aria-live | A polite live region with aria-busy, so a screen reader hears the answer arrive instead of sitting in silence. |