AI chat

Chat thread

Compound component: `ChatThread`, `ChatThread.Message`, `ChatThread.Empty`.

Client component2 exportsSource

Import

import { ChatThread, ChatThreadEmpty } from '@the_viveksingh/vivek-ui'

A scrolling thread

Sticks to the bottom as messages arrive, but stops sticking once the reader scrolls up - which is what keeps it from yanking them away from what they were reading.

Support session started.
Does the library work with Tailwind?
It does. The components ship plain CSS with zero specificity, so Tailwind utilities on the same element override them without a plugin or a preflight tweak.
And in Server Components?
<ChatThread
  style={{ height: '20rem' }}
  messages={messages.map((message) => ({
    id: message.id,
    role: message.role,
    name: message.name,
    content: message.text,
    timestamp: message.at,
    status: message.status,
  }))}
/>

While a reply streams

loading appends a typing indicator and announces it politely.

Support session started.
Does the library work with Tailwind?
It does. The components ship plain CSS with zero specificity, so Tailwind utilities on the same element override them without a plugin or a preflight tweak.
And in Server Components?
Assistant is typing
<ChatThread messages={messages} loading loadingLabel="Assistant is typing" />

Empty

Import ChatThreadEmpty by name rather than reaching for ChatThread.Empty. Dot access on a Client Component reads as undefined from a Server Component, so the named export is the one that works in both places.

No messages yet. Ask anything.
import { ChatThread, ChatThreadEmpty } from '@the_viveksingh/vivek-ui'

<ChatThread
  emptyState={<ChatThreadEmpty>No messages yet. Ask anything.</ChatThreadEmpty>}
/>

Props

Generated from the package's own type declarations, so this table cannot drift from the code.

Props for ChatThread
PropTypeDefaultDescription
messagesChatThreadMessage[]Data-driven transcript. When omitted, `children` is rendered instead.
autoScrollbooleanKeep the newest content in view. Defaults to `true`. It sticks to the bottom only while the user is already at the bottom — scrolling up to re-read something is never interrupted. See the note on the component.
loadingbooleanShow the typing indicator under the transcript.
loadingLabelstringLabel for that indicator. Defaults to `TypingIndicator`'s own.
emptyStateReactNodeRendered instead of an empty transcript.
labelstringAccessible name of the log region. Defaults to `Conversation`.
stickThresholdnumberHow close to the bottom, in px, still counts as "at the bottom". Defaults to 48 - enough to survive sub-pixel rounding and a half-visible line.

Every remaining prop is spread onto the root element, so all standard HTML and ARIA attributes work. className and style are merged with the library's own, never replaced, and the ref forwards to the root DOM node.

Exports

ChatThreadChatThreadEmpty

Rendering