Streaming response
FreeMarkdown-like text that renders cleanly while it is still arriving.
pnpm dlx shadcn@latest add https://caliperui.site/r/streaming-response.jsonReview of the counter change
The change is safe to merge with one note. It adds a step prop, caps the value at maxCount, and announces the new count to screen readers.
- The functional update avoids stale state when you click quickly.
- Disabling the button at the cap is clearer than silently ignoring clicks.
maxCountis declared after the component. That works, but moving it above would read better.
Here is a small test you could add:
it("stops at the maximum", () => { render(<Counter start={98} step={5} />); userEvent.click(screen.getByRole("button", { name: "Add" })); expect(screen.getByText("Count: 99")).toBeInTheDocument();});See the testing notes for the full checklist.
When to use #
Use it for
- Showing a model's answer token by token without layout jumps.
- Mixed paragraphs, lists and code blocks.
Skip it when
- The full text is known up front; render it directly.
Installation #
pnpm dlx shadcn@latest add https://caliperui.site/r/streaming-response.json
Adds its 5 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage #
This is the code behind the preview above. It is built from StreamingResponse and parseBlocks.
import { useState } from "react" import { StreamingResponse } from "@/components/ui/streaming-response" const after = `import { useState } from "react"; export function Counter({ start = 0, step = 1 }: { start?: number; step?: number }) { const [count, setCount] = useState(start); const label = \`Count: \${count}\`; function increment() { setCount((current) => Math.min(current + step, maxCount)); } return ( <div className="counter"> <p aria-live="polite">{label}</p> <button onClick={increment} disabled={count >= maxCount}> Add </button> </div> ); } export const defaultStart = 0; export const maxCount = 99; `; const reply = `## Review of the counter change The change is **safe to merge** with one note. It adds a \`step\` prop, caps the value at \`maxCount\`, and announces the new count to screen readers. - The functional update avoids stale state when you click quickly. - Disabling the button at the cap is clearer than silently ignoring clicks. - \`maxCount\` is declared after the component. That works, but moving it above would read better. Here is a small test you could add: \`\`\`ts it("stops at the maximum", () => { render(<Counter start={98} step={5} />); userEvent.click(screen.getByRole("button", { name: "Add" })); expect(screen.getByText("Count: 99")).toBeInTheDocument(); }); \`\`\` See [the testing notes](https://example.com/notes) for the full checklist.`; export function StreamingResponseDemo() { const [shown] = useState(reply.length); const [running] = useState(false); return ( <StreamingResponse text={reply.slice(0, shown)} streaming={running} pending={running && shown === 0} /> ); }
API reference #
StreamingResponse
component
parseBlocks
component
No props of its own; it accepts the native props of its root element.
Accessibility #
It is built on native elements, so roles and focus come from the browser.
No custom key handling; Tab and the native activation keys do the work.
Motion #
It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.