Conversation
Keeps a scrollable message history at the latest response until the reader scrolls back.
Preview
In action
History
Scroll back, then add a reply. Your place stays fixed until you choose Jump to latest.
Recorded messages · No model connected
When to use
Use it for
- A message history whose contents grow during a streamed response.
- Keep MessageComposer outside the scrollable history.
- Set autoScroll={false} to keep scrolling entirely manual.
- CSS-only markup provides native scrolling; following and the jump action require React or application code.
Avoid
- Putting an entire workbench in the message log.
- Using this component to fetch messages, render Markdown, or own a model connection.
Install
React package
npm install @noorddev/vlak-react
Load the stylesheet once at your app root, then import the components you use.
import "@noorddev/vlak-react/css";
import { Conversation, MessageComposer, Response, ResponseActions, type ComposedMessage } from "@noorddev/vlak-react";Per-component imports are available at @noorddev/vlak-react/components/conversation.
Copy the source
npx @noorddev/vlak-cli add conversation
Adds the component and its dependencies to components/vlak/. Follow the StyleX setup to compile the source in your application.
shadcn registry
npx shadcn add https://vlak.dev/r/conversation.json
Installs the same source through the shadcn CLI.
React
import { Conversation, MessageComposer, Response, ResponseActions, type ComposedMessage } from "@noorddev/vlak-react";
export function DocumentConversation({ onSend, generating, onStop }: {
onSend: (message: ComposedMessage) => void | Promise<void>;
generating: boolean;
onStop: () => void;
}) {
return <>
<Conversation label="Document conversation" maxHeight="28rem">
<Response from="user">Summarize this document.</Response>
<Response actions={<ResponseActions text="The document has three sections." />}>
<p>The document has three sections.</p>
</Response>
</Conversation>
<MessageComposer compact maxRows={6} sendOnEnter onSend={onSend} generating={generating} onStop={onStop} />
</>;
}See the integration example to compose these components with your application's model and data.
Props
Conversation
A message history that follows new content without taking the reader away from earlier messages.
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | "Conversation" | Accessible name of the scrollable message history. |
maxHeight | Property.MaxHeight<string | number> | "28rem" | Maximum height of the message history, before it scrolls. |
autoScroll | boolean | true | Follow new content while the reader is at the end. |
latestLabel | string | "Jump to latest" |
Also accepts HTMLAttributes<HTMLDivElement>.
The ref reaches HTMLDivElement.
Keyboard
| Keys | Does |
|---|---|
| Tab | Focuses the scrollable log and its interactive contents |
| Arrow keys, Page Up, Page Down | Scrolls the focused history using native browser behavior |
| Enter, Space | Activates Jump to latest and returns focus to the history |
Accessibility
- The message log has a supplied accessible label and a keyboard entry point.
- The log uses aria-live=off; use Response status announcements rather than announcing every streamed token.
- New content follows only while the reader is within 24px of the end. Scrolling back preserves the reading position.
- Jump to latest is a native 44px Button. Automatic scrolling is immediate and never moves focus.
- The ref and native attributes reach the outer div; maxHeight sets the inner scrollable history.
Markup
The same styles are available as CSS. Native HTML provides the static presentation; React or application code supplies state updates and actions.
npm install @noorddev/vlak
import "@noorddev/vlak/css";
<div class="rs-conversation"><div class="rs-conversation-viewport" role="log" aria-label="Conversation" aria-live="off" tabindex="0" style="max-height:28rem"><div class="rs-conversation-content"><p>You: Summarize this document.</p><p>Assistant: The document has three sections.</p></div></div></div>