Streaming text

Text arriving as it's written: words fading in, markdown that forms cleanly mid-stream, a soft-focus edge, a caret with stop and skip controls, and a table filling row by row.

Loading preview…

Install

  1. 1

    Run this command in your Tailwind CSS project. It adds components/ai/streaming-text-default.tsx and installs its dependencies.

    $ npx shadcn@latest add https://gradientdeck.com/ai-components/r/streaming-text-default.json

Build a streaming chat interface in React →

Usage & API

Example

streaming-text-default.tsx
import { StreamingText } from "@/components/ai/streaming-text-default"

<StreamingText
  text="…"
  shown={1}
  done={true}
/>

Props StreamingText

PropType & descriptionRequired
textstringRequired
shownnumberHow many words are visible so far.Required
donebooleanRequired

Dependencies

  • React
  • Tailwind CSS
  • motion
  • lucide-react
Implementation notes

Default exports StreamingText. Words already counted in shown are visible. A whitespace token is included only when the word count has passed it, so you do not get a leading gap. The caret is aria-hidden and blinks on a 0.9 second loop. done removes it. There is no stop button and no speed. You stop by freezing shown and setting done, or by leaving done false and simply not incrementing. The demo's reply is a paragraph about tides, and a replay control lives outside the component. Reduced motion skips the fade and still shows the words. aria-busy is true until done.

Choosing a layout

Use Default for prose that should feel like typing without being a terminal. Use Markdown when the model streams headings, bullets and fences and you cannot wait until the end to parse, because a half-written hash would otherwise flash as a paragraph. Use Soft focus when the leading edge should look soft rather than carry a caret. The demo is two paragraphs on why bread goes stale, revealed on an uneven gap pattern. Use Caret and controls when the person must stop, resume, skip to the end, or change speed before the reply finishes. Use Table rows when the answer is a comparison grid. The demo columns are a blank corner, LNER train, Flight and Caledonian Sleeper, and the rows cover door to door time, fare, CO2, luggage and working on board.

Message is the layout once you have a finished node. Text shimmer is the status before the first token. Message error replaces the stream when it fails, and its stopped variant is a smaller notice, Stopped by you, plus Continue. The caret controls already say Stopped and offer Continue and Skip to end, so you rarely need both on the same reply. Copy on the markdown, blur and table demos is labelled Copy reply. The caret's copy button says Copy, then Copied. Message actions is the richer row, with thumbs, for after done is true.

shown means words in one variant and characters in another. Which is it?

Default and Soft focus count words. Caret counts characters, and the demo moves that count at 26, 60 or 150 characters per second depending on Slow, Normal or Fast. Table does not use shown. It uses cells for finished cells and chars for the cell being typed. Markdown does not use a count either. You append to source and flip complete when the stream ends. Passing a word count into CaretStream will reveal only that many characters.

A markdown heading flashes as a hash and then jumps into a title.

That happens if you render the raw source yourself. StreamingMarkdown drops a final line that is only #, ##, ###, - or a short backtick run, until more text arrives. A finished heading line becomes a heading. If complete is true, the last line is not treated as a tail, so a real heading at the end is kept. Fences open the chrome early on purpose, with an empty language until the opening line finishes, and Copy stays disabled while open is true so a half-written block is not copied.