Skip to content

ChatList

ChatList

<ChatList style={{ height: 480 }}>
{messages.map((m) => (
<ChatMessage key={m.id} role={m.role} avatar={<Avatar fallback={m.avatar} />} time={m.time}>
{m.text}
</ChatMessage>
))}
{isStreaming ? <ChatMessage role="assistant" pending>{streamedText}<StreamingIndicator /></ChatMessage> : null}
</ChatList>

Install: @helixui/core

import { ChatList } from '@helixui/core'

status: stable · since: 0.3.0

Tags: messages, scroll, sticky-bottom, feed

Anatomy

┌────────────────────────┐
│ [old messages above] │ ↑ scrolls
│ ChatMessage │
│ ChatMessage │
│ ChatMessage ← new │ ← stays pinned to bottom unless user scrolled up
└────────────────────────┘

Layout

  • displayflex
  • widthfill
  • heightfill
  • intrinsicSizefills available height, scrolls
  • stackablefalse
  • fullBleedfalse

Visual

A vertically scrolling column that auto-pins to the bottom when new messages arrive — but only if the user is already near the bottom. If they scrolled up to read history, new messages do not yank them back.

Props

NameTypeDefaultDescription
autoScrollbooleantrueAuto-scroll on children change (only if user is near the bottom).
labelstringConversationaria-label for the log container.

Slots

  • children — ChatMessage components

Tokens used

space.4, font.family.sans

Accessibility

Role: log

Notes

  • Container is role="log" with aria-live="polite". New messages are announced once.
  • The sticky-to-bottom behaviour preserves the user’s reading position when they scroll up.

Composes with

ComponentRelationNote
ChatMessagechildEach child is a ChatMessage.
ChatHeadersiblingSits below ChatHeader.
ChatComposersiblingSits above ChatComposer.
TypingIndicatorsibling

Prompt examples

These are the AI prompt → JSX mappings used by the helixui prompt DSL and integrations like Cursor / Claude Code.

render an AI conversation

“list of chat messages with sticky-bottom”

<ChatList>
{messages.map(m => <ChatMessage key={m.id} role={m.role}>{m.content}</ChatMessage>)}
</ChatList>