Response
Shows an agent's answer as it streams in.
Examples
Order ord_4812 qualifies for a full refund.
- Shipped damaged
- Inside the 30-day window
Refunds are capped at $200 per order, see the refund pol
Refunding now.
Refund $48.00 on ord_4812Back to Maya Chen on the original payment method
Sizes
Refunds are capped at $200 per order.
defaultdefault
Refunds are capped at $200 per order.
sm
Tag
Refunding now.
Refund $48.00 on ord_4812Back to Maya Chen on the original payment methodcarddefault
Refunding now.
Preparing refundwaiting
Refunding now.
broken
Done. <notes>v2.4</notes>
unknown
States
Refunds are capped at $200 per order.
settled
Refunds are capped at $200 per
streaming
Refunding now.
Preparing refundtag pending
Usage
When to use it
- Use
- An agent answer arrives as a stream of markdown.
- Use
- Generated text carries structured cards (a refund, a ticket) inside it.
- Not for
- The text is the model thinking aloud. Use Reasoning.
Rules
9 rules
- Must
- Pass the whole text so far as children and keep streaming true until the end. Because any change that is not an append resyncs, so passing a delta replaces the answer with its last chunk.
- Must
- Register every card in tags with a pending label and a renderer that takes the parsed JSON. Because only registered names open a tag, so an unregistered card prints its raw tag and JSON into the answer.
- Must
- Define the inlineTags array at module scope or in useMemo. Because a new array each render breaks the memo on every frozen chunk, so the whole answer re-parses on each token.
- Should
- Write a tag pending label as an -ing verb and its object, two or three words, no ellipsis.
- Never
- Wrap a streaming Response in aria-live; leave announcements to Conversation. Because a live region reads every paced slice aloud, and Conversation already announces the finished turn.
- Never
- Add a fake typewriter; pacing already runs on the real stream. Because a second pace holds back text that already arrived and ignores the reduced-motion skip.
- Should
- Keep maxCodeLines at its default of 40; longer fences get a Show more lines button.
- Should
- Use size="sm" for side panels; keep default for an answer in a turn. Reasoning sets its own body styles.
- Never
- Set a Response heading above type-heading-xs. Because display type inside an answer outranks the page and the turn header, so the answer reads as a new page.
Accessibility
5 checks
- The root has no role and sets aria-busy while streaming.
- Headings render as h3 to h5, below the turn.
- Tables render as table with th scope="col".
- Links stay inline text links; code and tables scroll by keyboard and touch.
- Never put a live region on the answer.
Edge cases
9 edge cases
- Long hash or URL
- Inline code and links break anywhere; a code block caps at maxCodeLines.
- Empty or whitespace
- Renders no blocks, so leading newlines draw no empty paragraph.
- Table at 160px
- Tables scroll on paper and code inside its well; the page does not.
- Right to left
- Text and lists align right; the quote indent moves to the right edge.
- Unsafe link
- javascript: and data: links render as plain labels.
- Broken JSON in a tag
- The value is discarded; raw JSON never reaches the page.
- Stream ends with an open tag
- The tag resolves as if it timed out; a JSON tag with a viable body keeps its placeholder until the value completes.
- Burst over 512 characters
- Paces in word-snapped 24ms slices; a non-append text resyncs at once.
- Reduced motion or hidden tab
- Every delta renders at once.
API
9 props
childrenstringdefaultrequiredstreamingbooleandefaultfalsepacebooleandefaulttruetagsRecord<string, TagRenderer>default{}inlineTagsInlineTag[]default[]tagTimeoutnumberdefault8000maxCodeLinesnumberdefault40size'default' | 'sm'default'default'showMoreLines(lines: number) => stringdefault"Show n more lines"
Tokens
14 tokens
--type-body- 340 14px/20px var(--font-sans)
--type-body-sm- 380 13px/18px var(--font-sans)
--text-1- var(--ink)
--type-heading-xs- 400 16px/20px var(--font-sans)
--type-label- 400 14px/16px var(--font-sans)
--surface-card- oklch(var(--grey-c))
--type-code- 400 13px/20px var(--font-mono)
--type-code-sm- 400 12px/16px var(--font-mono)
--type-tag- var(--type-code-sm)
--line-row- oklch(var(--ink-c) / 0.08)
--line-control- oklch(var(--ink-c) / 0.16)
--text-2- oklch(var(--ink-c) / 0.80)
--row- 40px
--row-sm- 32px