Search documentation

Find a vivid-layer page or product.

Streaming

Renders cumulative agent Markdown at a smooth, evenly paced reveal with a subtle animation for new content.

Fade In

Fades newly arrived characters into place.

pnpm dlx shadcn@latest add @vivid-layer/streaming

Blur In

Sharpens newly arrived characters from a soft blur.

pnpm dlx shadcn@latest add @vivid-layer/streaming

Slide Up

Moves newly arrived characters upward into place.

pnpm dlx shadcn@latest add @vivid-layer/streaming

Slide Left

Moves newly arrived characters left into place.

pnpm dlx shadcn@latest add @vivid-layer/streaming

Fold In

Unfolds newly arrived characters from a top hinge in 3D.

pnpm dlx shadcn@latest add @vivid-layer/streaming

Streaming renders an AI response the way modern chat apps do: cumulative Markdown revealed at a smooth, even pace, with a subtle animation on newly arrived text. It is a React component for LLM chat interfaces — hand it the growing string from your model stream and it handles pacing, Markdown, and motion, in shadcn/ui and Tailwind CSS projects.

API reference

PropTypeDefaultDescription
childrenstringProvides the complete Markdown accumulated for the current response.
isStreamingbooleanfalseEnables animation for content arriving during the active response.
animation"fadeIn" | "blurIn" | "slideUp" | "slideLeft" | "foldIn" | (string & {})"fadeIn"Selects a supplied animation, or the name of one you add yourself.
animationDurationnumber400Sets the Streamdown duration and the final animation linger window, in milliseconds.

Streaming also accepts standard div props except children. Those props apply to the public root; Streamdown remains an internal Markdown renderer.

Behavior

Pass the complete response received so far on every render. Network chunks arrive in uneven bursts, so Streaming buffers each update and reveals it a few characters per frame at a rate proportional to the backlog: the display trails the newest arrival by roughly a third of a second, speeds up when chunks race ahead, and quickly drains what remains once isStreaming turns false. Supplied animations use 400ms by default; custom effects can coordinate their CSS duration and final linger window through animationDuration. This pacing is the component's only behavior — there is no unsmoothed mode.

Fold In follows the same 400ms duration and adaptive reveal pacing as the other built-in variants. It unfolds each newly arrived character from a top hinge through a 700px perspective, then settles it into the normal text.

Text is revealed on grapheme boundaries, so CJK text, emoji, and combining marks never split mid-character. While the response grows, incomplete Markdown remains renderable and CJK punctuation works with emphasis and automatic links. Content already present when the component mounts renders immediately; pacing applies only to what arrives afterwards.

Reduced-motion preferences disable both the pacing and the animation, so content renders as soon as it arrives. When streaming ends and the reveal settles, the animation transformer is removed and the completed response renders without character-level animation spans.

The component intentionally does not manage messages, requests, retries, scrolling, persistence, code highlighting, math, or diagrams.

The public root defaults to dir="auto" and reflects the active response through aria-busy, which stays true until buffered content finishes revealing. The surrounding transcript owns announcements and controls such as Stop, Retry, and Copy.

Tailwind setup

Streamdown's preset typography is distributed from its package. Keep these sources in the Tailwind CSS entry file, adjusting the relative path when the file is nested differently:

@source "../node_modules/streamdown/dist/*.js";
@source "../node_modules/@streamdown/cjk/dist/*.js";

Do not also import streamdown/styles.css. Streaming installs its own animation CSS. Its Slide Up and Slide Left variants move inline tokens with relative offsets because transforms do not apply to inline boxes. Fold In temporarily makes arriving characters inline-block so its 3D transform can render. In particular, sd-slideUp intentionally differs from Streamdown's transform-based version.

Add an animation

Each animation is one self-contained block of installed CSS: a @keyframes rule and the mapping that selects it. Add the same pair for a name of your own, then pass that name to animation. The prop type accepts any string, so this needs no change to the component:

@keyframes sd-wipeIn {
  from {
    opacity: 0;
    clip-path: inset(0 100% 0 0);
  }

  to {
    opacity: 1;
    clip-path: inset(0 0 0 0);
  }
}

[data-slot="streaming"][data-sd-animation="wipeIn"] [data-sd-animate] {
  animation-name: sd-wipeIn;
}

Animate properties that apply to inline elements. opacity, filter, clip-path, color, and relative offsets work; transform is ignored unless you also make the token a block box, which changes where lines break.

Credits

The concise text-animation API is informed by Magic UI Text Animate. The cumulative Markdown contract follows the approach used by AI Elements Message and Streamdown Animation. The smooth pacing addresses the same problem as the AI SDK's smoothStream transform, but on the client, so it works with any transport. This component exposes an independent Vivid Layer API and does not depend on AI Elements or the AI SDK. Fold In adapts the top-hinged 3D motion language of React Bits Fold Text without adding GSAP or ScrollTrigger.