Streaming Text
Token-streaming text whose tail fades as if still being machined, with a blinking caret slab and aria-busy semantics.
@emboss/streaming-text
Installation
pnpm dlx shadcn@latest add @emboss/streaming-textUsage
import { StreamingText } from "@/components/ui/streaming-text";
export function Example({ partial, done }: { partial: string; done: boolean }) {
return <StreamingText text={partial} isStreaming={!done} />;
}API reference
StreamingText
| Prop | Type | Default | Description |
|---|---|---|---|
| text | string | — | The text received so far. |
| isStreaming | boolean | false | While true: the tail fades, the caret blinks, and aria-busy is set. |
| Data attribute | Description |
|---|---|
| data-streaming | Present while text is still arriving. |
Source
View source — streaming-text.tsxHide source — streaming-text.tsx
"use client";
import { cn } from "@/lib/utils";
/**
* Text that arrives in chunks. While streaming, the tail of the passage
* fades as if still being machined and a caret slab blinks at the end; no
* per-character animation, no layout thrash. The container reports
* aria-busy so assistive tech can wait for the settled result — pair it
* with a `role="log"` parent for polite announcements.
*
* @example
* <StreamingText text={partial} isStreaming={!done} />
*/
function StreamingText({
className,
text,
isStreaming = false,
...props
}: Omit<React.ComponentProps<"div">, "children"> & {
/** The text received so far. */
text: string;
/** Whether more is still arriving. */
isStreaming?: boolean;
}) {
return (
<div
data-slot="streaming-text"
aria-busy={isStreaming || undefined}
data-streaming={isStreaming ? "" : undefined}
className={cn(
"text-sm leading-relaxed whitespace-pre-wrap text-ink",
isStreaming &&
"[mask-image:linear-gradient(to_bottom,black_calc(100%-1.25em),oklch(0_0_0/0.35))]",
className,
)}
{...props}
>
{text}
{isStreaming ? (
<span
aria-hidden
className="ms-0.5 inline-block h-[1em] w-[0.45em] translate-y-[0.15em] animate-depth-pulse rounded-2xs bg-well shadow-deboss-1 motion-reduce:animate-none"
/>
) : null}
</div>
);
}
export { StreamingText };