Components

Message scroller

Transcript scrolling and message anchors

Import
import {
  MessageScrollerProvider,
  MessageScroller,
  MessageScrollerViewport,
} from "@nyte-ai/ui/message-scroller";

A transcript scroll controller for ID jumps and streaming content.

Usage

  • Wrap the root, controls and hook consumers in one MessageScrollerProvider.
  • Set the viewport height and overflow with className or style, not xstyle. Parts have no default layout.
  • scrollToMessage only reaches mounted items with a matching messageId.
  • MessageScrollerButton uses Nyte button styles unless render replaces it.

Anatomy

PartElementParent
MessageScrollerProviderContext onlyTranscript and hook consumers
MessageScrollerdivProvider
MessageScrollerViewportdivRoot
MessageScrollerContentdiv plus internal spacerViewport
MessageScrollerItemdivContent
MessageScrollerButtonbutton, or renderProvider

Examples

const messages = (
  <MessageScrollerContent>
    <MessageScrollerItem messageId="welcome">Hello.</MessageScrollerItem>
  </MessageScrollerContent>
);
const viewport = (
  <MessageScrollerViewport style={{ height: 240, overflowY: "auto" }}>
    {messages}
  </MessageScrollerViewport>
);
<MessageScrollerProvider autoScroll>
  <MessageScroller>{viewport}</MessageScroller>
</MessageScrollerProvider>

Props

DOM parts take native props and refs and set a matching data-slot. Button render callbacks get active and direction state.

PartPropTypeDefault
ProviderautoScrollbooleanfalse
ProviderdefaultScrollPosition"start" | "end" | "last-anchor""end"
ProviderscrollEdgeThresholdnumber8
ProviderscrollPreviousItemPeeknumber64
ProviderscrollMarginnumber0
ViewportpreserveScrollOnPrependbooleantrue
ContentspacerClassNamestringNone
ItemmessageIdstringNone
ItemscrollAnchorbooleanfalse
Buttondirection"start" | "end""end"
ButtonbehaviorScrollBehavior"smooth"
Buttonvariant"default" | "outline" | "secondary" | "ghost" | "destructive" | "link" | null"secondary"
Buttonsize"default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null"icon-sm"

The button sets data-direction, data-variant, data-size and data-active. Without children, it shows an arrow and a visually hidden "Scroll to end" or "Scroll to start" label. Items set data-message-id and data-scroll-anchor="true" or "false".

Hooks

HookResult
useMessageScroller()scrollToStart(options?), scrollToEnd(options?), scrollToMessage(messageId, options?); each returns boolean
useMessageScrollerScrollable(){ start: boolean, end: boolean }
useMessageScrollerVisibility(){ currentAnchorId: string | null, visibleMessageIds: string[] }

MessageScrollerScrollOptions has align: "start" | "center" | "end" | "nearest", behavior: ScrollBehavior and scrollMargin: number.

The headless scroller reference covers controller behavior.

Accessibility

The viewport defaults to role="region", aria-label="Messages" and tabIndex={0}. Content defaults to role="log" and aria-relevant="additions". Inactive scroll buttons are inert. Use behavior="auto" to scroll without animation.