ReScript Shadcn
Get started
  • Installation
Components
  • Accordion
  • Alert
  • AlertDialog
  • AspectRatio
  • Attachment
  • Avatar
  • Badge
  • Breadcrumb
  • Bubble
  • Button
  • ButtonGroup
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Collapsible
  • Combobox
  • Command
  • ContextMenu
  • DataTable
  • DatePicker
  • Dialog
  • Direction
  • Drawer
  • DropdownMenu
  • Empty
  • Field
  • HoverCard
  • Input
  • InputGroup
  • InputOtp
  • Item
  • Kbd
  • Label
  • Marker
  • Menubar
  • Message
  • MessageScroller
  • NativeSelect
  • NavigationMenu
  • Pagination
  • Popover
  • Progress
  • RadioGroup
  • Resizable
  • ScrollArea
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Skeleton
  • Slider
  • Sonner
  • Spinner
  • Switch
  • Table
  • Tabs
  • Textarea
  • Toast
  • Toggle
  • ToggleGroup
  • Tooltip
  • Typography

Message Scroller

A chat scroll container for message transcripts.

How can I help you today?
Static transcript preview
Can you check whether the docs examples still compile?
Yes. I added the missing preview components, regenerated the loader, and kept the class hooks aligned with upstream.
Great. Please run classname and pixel checks next.
Running visual checks

MessageScroller is a chat transcript scroller built on the @shadcn/react primitives. It provides the styled ReScript surface for opening position, anchored turns, scroll preservation, and jump controls while leaving message rendering to Message, Bubble, Marker, and Attachment.

Installation#

npx shadcn@latest add @rescript-shadcn/MessageScroller

Usage#

<MessageScroller.Provider defaultScrollPosition=LastAnchor>
  <MessageScroller>
    <MessageScroller.Viewport>
      <MessageScroller.Content>
        <MessageScroller.Item messageId="question" scrollAnchor=true>
          <Message align=End>
            <Message.Content>
              <Bubble>
                <Bubble.Content> {"Can you check the docs?"->React.string} </Bubble.Content>
              </Bubble>
            </Message.Content>
          </Message>
        </MessageScroller.Item>
      </MessageScroller.Content>
    </MessageScroller.Viewport>
    <MessageScroller.Button ariaLabel="Scroll to latest" />
  </MessageScroller>
</MessageScroller.Provider>

MessageScroller fills its parent, so place it inside a height-constrained container.

Composition#

MessageScroller.Provider
└── MessageScroller
    ├── MessageScroller.Viewport
    │   └── MessageScroller.Content
    │       ├── MessageScroller.Item
    │       └── MessageScroller.Item
    └── MessageScroller.Button
  • MessageScroller.Provider owns scroll state and behavior props.
  • MessageScroller is the styled frame.
  • MessageScroller.Viewport is the scrollable element.
  • MessageScroller.Content holds transcript rows.
  • MessageScroller.Item wraps every direct row so the primitive can measure, anchor, preserve position, and jump to it.
  • MessageScroller.Button scrolls to the start or end and is inert when there is no content in that direction.

Core Concepts#

Anchoring Turns#

Mark the row that starts a meaningful turn with scrollAnchor=true. Use stable messageId values so the scroller can preserve and target rows.

<MessageScroller.Item messageId=message.id scrollAnchor={message.role == "user"}>
  <Message />
</MessageScroller.Item>

Opening Position#

Set defaultScrollPosition=LastAnchor to open a saved transcript at the latest anchored turn instead of the absolute bottom.

Loading Earlier Messages#

MessageScroller.Viewport preserves the visible row when older messages are prepended. This is enabled by default with preserveScrollOnPrepend=true.

Accessibility#

MessageScroller.Viewport is a keyboard-focusable scroll region by default in the upstream primitive. MessageScroller.Content uses log-region defaults for additions. Pass ariaBusy=true while a turn streams if announcements should wait for the completed row.

API Reference#

MessageScroller.Provider#

PropTypeDefault
autoScrollboolfalse
defaultScrollPositionStart | End | LastAnchorEnd
scrollEdgeThresholdfloat-
scrollPreviousItemPeekfloat-
scrollMarginfloat-

MessageScroller.Item#

PropTypeDefault
messageIdstring-
scrollAnchorboolfalse

MessageScroller.Button#

PropTypeDefault
directionStart | EndEnd
behaviorAuto | Instant | Smooth-
@react.component
let make = () =>
  <Card className="h-[480px] w-full max-w-xl">