Skip to content

Conversation

Streaming Message

Assistant message with token streaming, collapsible reasoning, citation chips and copy / retry actions.

3 variantsMIT

Preview

Claude Opus 5.5Responding…

The 429s come from your org's output tokens per

Installation

Copies the component into components/ui and installs its dependencies and theme tokens.

pnpm dlx shadcn@latest add https://tuffui.com/r/streaming-message.json

Usage

Pass your own data and wire the callbacks to your agent runtime.

import { StreamingMessage, type StreamingMessageData } from "@/components/ui/streaming-message";

export function AssistantTurn({ message }: { message: StreamingMessageData }) {
  // Append text deltas to message.content as they arrive and flip status to
  // "done" on message_stop (or "error" if the stream breaks).
  return (
    <StreamingMessage
      data={message}
      model="Claude Opus 5.5"
      onCopy={(text) => analytics.track("copy_message", { length: text.length })}
      onRetry={() => chat.regenerate(message.id)}
    />
  );
}

Props

PropTypeDefaultDescription
data*StreamingMessageData—The message: content so far, status, optional reasoning, citations and error. Update it on every delta.
modelstring"Assistant"Name shown in the message header.
showHeaderbooleantrueShow the avatar and model row. Turn it off inside a thread that already labels each turn.
onCopy(content: string) => void—Called after the content is copied to the clipboard.
onRetry() => void—Shows Retry on errors and Regenerate when done.
classNamestring—Extra classes for the root article.
type StreamingMessageData = {
  content: string;          // paragraphs split on blank lines; `code` and **bold** supported
  status: "streaming" | "done" | "error";
  reasoning?: {
    text: string;
    streaming?: boolean;    // shows "Thinking…" and keeps the panel open
    durationMs?: number;    // "Thought for 4s" once finished
  };
  citations?: { id: string; title: string; url: string; source?: string }[];
  error?: string;
};