Conversation

Holds a thread that follows new replies.

Row

  • You
    Refund order 4812, the headphones arrived broken.

    userdefault

  • Refund agent

    Refunded $48.00 to the original card.

    Worked for 14s,14:02glaze-large

    assistant

  • AgentRunning
    Thinking

    thinking

  • Retrying nowattempt 2The model provider is busy.

    retry

  • Lost the connectionCheck the connection and retry.

    error

  • divider

  • 3 files changed42 added 7 removed

    diff

States

  • You
    Refund order 4812.
    Refund agent

    Refunded $48.00 to the original card.

    Worked for 14s,14:02glaze-large

    idle

  • You
    Refund order 4812.
    AgentRunning
    Thinking

    streaming

  • You
    Refund order 4812.

    stopped

  • You
    Refund order 4812.
    Rate limited by the model providerTry again in a minute.

    error

Usage

When to use it
Use
An agent streams replies into a thread and the view should follow them until the person scrolls away.
Use
A thread mixes turns, a thinking row, retries, errors and dividers.
Not for
A static transcript that never grows. Use Message.
Not for
A log of lines with levels and search. Use Code block.

Rules

6 rules
Must
Set status to streaming while a turn runs and to idle, stopped or error when it ends. Because it drives aria-busy, the height floor that keeps the view from jumping, the thinking row and the one announcement per turn end.
Must
Call snap() from useConversation when the person sends; wrap the composer in ConversationProvider to reach it. Because the view lets go only on a gesture, so a person reading earlier turns would send and not see their own message.
Never
Scroll the viewport yourself on every delta. Because it fights the person who scrolled up to read; the viewport follows while attached and lets go only on a wheel, swipe, key or scrollbar drag up.
Should
Pass rows to ConversationTimeline as typed data and keep each row object stable. Because structurally equal rows keep their identity, so a delta re-renders only the row it touched.
Must
Show a stopped turn as a divider row with reason "interrupted", never as an error row. Because the person stopped it on purpose, and an error says something broke.
Never
Put the thread in a card or a panel; it is a scroll column on the page. Because the person's bubbles are the only boxes in a thread, and a shell around it adds a frame to everything inside.

Accessibility

4 checks
The viewport is role="log" with aria-live off and one status region that says complete, stopped or failed when a turn ends.
The viewport is focusable with the inset ink ring; ArrowUp, PageUp and Home let go of the end, End returns.
Jump to latest is inert while near the end; when it hides with focus, focus moves to the thread.
Waiting on you goes to the waiting call and focuses its first action, Deny.

Keyboard

2 keys
ArrowUp, PageUp, Home
Scroll up and stop following.
End
Returns to the end and follows again.

Edge cases

5 edge cases
Selecting text while it streams
Following pauses until the selection collapses.
A loader replaced by shorter text
The height floor holds while streaming, so the view never jumps.
History prepended above
The row under the reader stays where it was.
Long thread
Offscreen rows skip layout; the last user row and the last row are always laid out.
Reduced motion
Jumps scroll at once instead of smoothly.

API

8 props
status
'idle' | 'streaming' | 'stopped' | 'error' default 'idle'
ConversationViewport aria-label, labels
string, {complete, stopped, failed} default "Conversation"
ConversationTimeline rows
ConversationRow[] default required
ConversationTimeline renderRow, onRetry, labels
functions, ConversationLabels
ConversationJump waiting
boolean default true
ConversationItem rowKey, pinned
string, boolean
useConversation()
{attached, distance, atEnd, scrollToEnd, snap, detach}
useStickToBottom(ref)
hook

Tokens

9 tokens
--page
#fbfbfa
--fail-tint
#fbe9e5
--fail-text
#b23c29
--radius-overlay
10px
--control
32px
--row
36px
--z-raised
10
--focus
rgb(5 5 5 / 0.80)
--dur-state
160ms

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.