Word stream

A chat reply that types in word by word behind a caret, then hands back when done.

Behaviour

  • Every word is rendered up front at opacity 0 and revealed by an attribute write per tick, so there is no React render per word and the paragraph never reflows.
  • Spaces, tabs and newlines are kept (whitespace-pre-wrap) and unbreakable tokens such as hashes wrap (wrap-anywhere); the caret pill rides after the newest word at full opacity (outside the word's fade), takes no width so it never pushes a word onto the next line, and blinks if the stream stalls.
  • Each word fades in over 150ms. The pace follows elapsed time, not timer ticks, so a reply started in a background tab catches up when you return.
  • The visible words are aria-hidden and the whole text sits once in an sr-only copy, so a live region announces the reply once.
  • Changing text or interval restarts the reveal (words and caret cleared at once); the interval is cleared on unmount.
  • Reduced motion: the whole text shows at once in one 200ms fade with no caret (CSS, so server HTML is unchanged) and onDone fires on mount.

Props

PropTypeDescription
textstringThe full text, already known. Spaces and newlines are kept.
intervalnumberdefault 45Milliseconds between words.
onDone() => voidCalled once when the last word is shown (on mount under reduced motion).
classNamestringMerged onto the root <p>; set size, leading and colour here.

Installation

npx shadcn@latest add https://purrfectui.com/r/word-stream.json

How to use

import { WordStream } from "@/components/ui/word-stream";

<WordStream text={reply.body} onDone={() => scrollToBottom()} />
<WordStream text={summary} interval={30} className="text-sm leading-relaxed" />

Source code

Loading…

License

MIT. Copy it into your project and change it as you like.

0