Skip to content

Chat Message List

@astryxdesign/core v0.7.1 · ChatMessageList

How should I structure a monorepo?
Use workspaces with a shared packages directory. Keep each package focused on a single concern.
Should I use Yarn or pnpm for that?

Usage ​

Presentational message container with density context and infinite scroll support. Provides role="log" with aria-live="polite" for accessibility. A flex spacer pushes messages to the bottom when the list isn't full; set align="top" to start them at the top instead.

Rows go in the default slot: usually ChatMessage and ChatSystemMessage. density flows down to the rows that do not set their own. gap overrides the row gap alone, without touching padding, which is what LLM event streams with uneven spacing need.

scrollToTopAction runs when a sentinel at the top of the list enters the scroll parent, and a spinner stays up until the promise settles. Pair it with an ancestor that scrolls; inside ChatLayout the layout owns that scroll.

isStreaming marks the log aria-busy, so a screen reader waits and announces the finished message once instead of re-reading partial text on every token.

align only changes where a short list rests. bottom, the default, keeps the spacer so a short conversation sits just above the composer; top drops it for document-style transcripts. Once rows overflow the container the spacer collapses either way, so scrolling and the layout's auto-scroll-to-bottom behave the same in both modes.

vue
<script setup lang="ts">
import { ChatMessage, ChatMessageBubble, ChatMessageList } from '@astryx-vue/core/Chat'
</script>

<template>
  <ChatMessageList :is-streaming="true">
    <ChatMessage sender="user">
      <ChatMessageBubble>How should I structure a monorepo?</ChatMessageBubble>
    </ChatMessage>
    <ChatMessage sender="assistant">
      <ChatMessageBubble variant="ghost">
        Use workspaces with a shared packages directory.
      </ChatMessageBubble>
    </ChatMessage>
  </ChatMessageList>
</template>

Examples ​

Common configurations, variations, and states.

Density

Compact

How does density work?
A
Density provides default spacing at every level — message gap, bubble padding, and gap between child elements. Use gap to tune row spacing independently.

Balanced

How does density work?
A
Density provides default spacing at every level — message gap, bubble padding, and gap between child elements. Use gap to tune row spacing independently.

Spacious

How does density work?
A
Density provides default spacing at every level — message gap, bubble padding, and gap between child elements. Use gap to tune row spacing independently.

Side-by-side comparison of compact, balanced, and spacious densities. Use compact in sidebars or panels, balanced for most full-page chat, and spacious for long-form reading. Use gap when row spacing needs to differ from density.

Full Featured

useReducer.tsformState.ts
Can you review these files?
A
Sure! Here's the key pattern from useReducer.ts:
The reducer is pure and easy to test — pass in state and action, assert on the output.
    
tsx
const [state, dispatch] = useReducer(
(state, action) => ({
...state,
[action.field]: action.value,
}),
{ name: '', email: '' }
);
Agent shared a code snippet
That's clean, thanks!

Conversation showcasing system messages, multi-bubble grouping, markdown, code blocks, and metadata. Combines date dividers, ghost bubbles, grouped messages, and rich content in a single example.