Conversation
A typed transcript with reading-position preservation and app-owned composition.
Usage
import { Conversation } from '@commenda/pathfinder/conversation';
import { Message } from '@commenda/pathfinder/message';
<Conversation
messages={messages}
getMessageKey={(message) => message.id}
renderMessage={(message, { isLast }) => (
<Message
from={message.role}
parts={message.parts}
getPartKey={getPartKey}
renderPart={renderPart}
actionConfig={{
retry: { isLatest: isLast, onRetry: regenerate },
}}
/>
)}
announcement={turnAnnouncement}
footer={<Composer {...composerProps} />}
className="conversation-layout"
/>;Give the container a bounded height or flex: 1; min-height: 0 inside a bounded flex layout. Conversation owns its scrolling viewport, not the page scroll. Change its React key when switching conversation IDs.
Docked request and recap
The live question sits 12px above Composer. Acceptance appends a stable recap part to the originating reply and removes the docked card. Temporary dismissal keeps the question pending and provides a way to reopen it. Escape belongs to the focused nested overlay first; the request handles it only after the overlay closes. Typing in Composer never activates a question's numeric shortcuts.
Empty conversation
Supply emptyState with suggested prompts or introductory content. Message fetching, permissions and transport remain in the host.
History and stream following
Use Load earlier messages, scroll into the history, and choose Append reply or Stream chunk. The viewport follows only within 32px of the bottom. Earlier rows retain their keys and the first visible row retains its offset. Jump to latest resumes following. ResizeObserver also handles delayed image/font/content growth. Scroll positioning is immediate and respects reduced-motion users without animated jumps.
historyConfig reports loading and an optional inline error. The host guards overlapping loads, keeps previously loaded items, and retries via onLoadMore. This component does not virtualize an unbounded history: paginate on the server and profile realistic histories before adding a virtualization layer.
API reference
| Prop | Type | Behavior |
|---|---|---|
messagesRequired | readonly Item[] | Original typed application/SDK objects |
getMessageKeyRequired | (
message: Item,
index: number
) => string | Stable unique identity across prepend/append |
renderMessageRequired | (
message: Item,
context: {
index: number;
isLast: boolean;
}
) => ReactNode | Host maps roles, states and parts |
historyConfig | ObjectView fields | App-owned earlier-history loading |
emptyState | ReactNode | Empty transcript content |
footer | ReactNode | Composer, docked requests and related controls |
announcement | string | Concise turn-level live status |
label | string | Conversation and transcript accessible names |
className | string | Bounded layout; no default fixed height |
historyConfigObject fields
| Field | Type | Requirement |
|---|---|---|
hasMore | boolean | Required |
isLoading | boolean | Required |
onLoadMore | () => void | Required |
error | string | Optional |
Design rules and integration
- CONVERSATION-01: Preserve original objects and keyed part identity. Do not flatten text around tool calls or discard unknown custom parts.
- CONVERSATION-02: Do not pull a reader to the bottom while they inspect history. Preserve the visible row during prepend and delayed layout changes.
- CONVERSATION-03: The transcript is a keyboard-focusable region, not a per-token live log. Use
announcementfor “Response complete”, “Stopped” or an error once per transition. - CONVERSATION-04: Transport, providers, execution, feedback persistence and message storage remain in the application. Compose existing Pathfinder components through the footer and typed render callbacks.
The API follows the separation in Vercel AI Elements Conversation and Message, using Pathfinder's direct props instead of requiring compound children. Existing useChat messages can pass through unchanged; adapt rendering at the boundary. Migration of the existing application is out of scope. Download/export, virtualization and automatic message trimming are not performed here.