Streaming Response

Shows a reply as it arrives.

Installation

pnpm dlx shadcn@latest add @paneui/streaming-response

Usage

Examples

Markdown

Use when replies continue numbered lists, paste URLs or keep checklists.

Error

Controlled Feedback

Sources Open, No Actions

Use in read-only transcripts where the sources matter more than the actions.

Custom Content

ResponseMarkdown and StreamingCaret

Use these to build your own message surface with the same renderer and caret.

Markdown

The renderer covers paragraphs, # headings, bullet and numbered lists, fenced code, inline code, bold, italics, links and [n] citations. A numbered list keeps its first number (<ol start>), bare http(s) URLs become links without their trailing punctuation, and - [ ] / - [x] items show a read-only checkbox.

Layout

The actions row keeps its height while streaming (hidden and inert), so the reply doesn't jump when it completes. Sources appear only after streaming ends.

Motion

Under reduced motion the caret stops blinking and the actions and sources fade in without sliding.

API Reference

StreamingResponse

Surface for an assistant reply, with actions and sources under the text. Also accepts every prop of div.

text
string
Markdown subset (headings, lists, fenced and inline code, bold, italic, links, [n] citations), streamed in by appending.
children
ReactNode
Custom content instead of text.
status= "streaming"
"streaming" | "complete" | "error"
Reply state; streaming shows the caret and hides actions.
sources
CitationSource[]
Sources for [n] markers and the footer list. See Citations.
defaultSourcesOpen= false
boolean
Start with the sources list open.
copyText
string
Plain text for the copy action. Defaults to text.
onRetry
() => void
Adds a Regenerate action, and Retry on errors.
feedback
"up" | "down" | null
Controlled thumbs state.
defaultFeedback= null
"up" | "down" | null
Initial thumbs state when uncontrolled.
onFeedbackChange
(feedback: "up" | "down" | null) => void
Called when a thumb is toggled.
errorMessage= "The response was interrupted."
ReactNode
Message shown when status="error".
showActions= true
boolean
Show the copy / regenerate / feedback row.
contentClassName
string
Classes for the wrapper around the body.
PropTypeDefaultDescription
textstring—Markdown subset (headings, lists, fenced and inline code, bold, italic, links, [n] citations), streamed in by appending.
childrenReactNode—Custom content instead of text.
status"streaming" | "complete" | "error""streaming"Reply state; streaming shows the caret and hides actions.
sourcesCitationSource[]—Sources for [n] markers and the footer list. See Citations.
defaultSourcesOpenbooleanfalseStart with the sources list open.
copyTextstring—Plain text for the copy action. Defaults to text.
onRetry() => void—Adds a Regenerate action, and Retry on errors.
feedback"up" | "down" | null—Controlled thumbs state.
defaultFeedback"up" | "down" | nullnullInitial thumbs state when uncontrolled.
onFeedbackChange(feedback: "up" | "down" | null) => void—Called when a thumb is toggled.
errorMessageReactNode"The response was interrupted."Message shown when status="error".
showActionsbooleantrueShow the copy / regenerate / feedback row.
contentClassNamestring—Classes for the wrapper around the body.
data-status
The current status.
Data attributeDescription
data-statusThe current status.

ResponseMarkdown

The markdown-ish renderer behind text.

text*
string
Source text.
sources
CitationSource[]
Resolves [n] markers; needs a surrounding CitationGroup for numbering and jump-to-list.
caret= false
boolean
Append a StreamingCaret to the last block.
className
string
Merged onto the wrapper.
PropTypeDefaultDescription
text*string—Source text.
sourcesCitationSource[]—Resolves [n] markers; needs a surrounding CitationGroup for numbering and jump-to-list.
caretbooleanfalseAppend a StreamingCaret to the last block.
classNamestring—Merged onto the wrapper.

StreamingCaret

Blinking block caret for the tail of streamed text.

className
string
Merged onto the caret.
PropTypeDefaultDescription
classNamestring—Merged onto the caret.