Skip to content

Stream Response

Streamdown

Streaming markdown with a block caret, inline citations and a source strip. Stop and regenerate built in.

Install

$ npx shadcn@latest add https://spotcheckui.com/r/stream-response.json

Also installs streamdown.

Usage

"use client"

import { StreamResponse } from "@/components/spotcheck/stream-response"
import { useStream } from "@/hooks/use-stream"

export function Answer() {
  const { text, sources, status, error, start, stop, regenerate } = useStream("/api/chat")

  return (
    <StreamResponse
      text={text}
      sources={sources}
      status={status}
      error={error}
      onStop={stop}
      onRegenerate={() => regenerate()}
    />
  )
}

useStream reads the AI SDK's UI message stream with no ai package on the client. Return createUIMessageStreamResponse or toUIMessageStreamResponse from your route and it works. Anything else is read as plain SSE, where each data: line is more text. It also returns reset, which cancels the stream and clears it.

Use SavedResponse to show a finished reply again, with the same formatting and sources.

Write [1] in the text to cite the first source. Screen readers hear one finished sentence at a time, not every token.

Props

  • text * RequiredstringDefault —The text so far. Markdown is rendered as it arrives, and [1] becomes a citation.
  • sourcesStreamSource[]Default []
  • status * RequiredStreamStatusDefault —
  • onStop() => voidDefault —Shows a Stop button while streaming.
  • onRegenerate() => voidDefault —Shows a Regenerate button once it's finished.
  • errorstring | nullDefault —Shown if the stream fails.
  • classNamestringDefault —

Keyboard

TabReach citations, sources and buttons

Reduced motion

The caret stays still. Text still arrives as it streams.