Skip to content

Message Scroller

Message Scroller is a six-part composite over the Navius MessageScroller primitive: anchored turns that keep part of the previous message in view, an AutoScroll follow that disengages the moment the reader scrolls away, prepend preservation for loading older history, and jump-to-message. It is a chat extra outside the Base UI primitive set (no Radix/Base UI counterpart), the same precedent as DataGrid. ZitsMessageScrollerProvider renders NaviusMessageScrollerProvider (no DOM; owns scroll state), ZitsMessageScroller renders NaviusMessageScroller (the frame), ZitsMessageScrollerViewport renders NaviusMessageScrollerViewport (the scroll container, needs a bounded height from its ancestors), ZitsMessageScrollerContent renders NaviusMessageScrollerContent, ZitsMessageScrollerItem renders NaviusMessageScrollerItem (one per row), and ZitsMessageScrollerButton renders NaviusMessageScrollerButton (the scroll-to-start/end control). The styled item slot ships in the chat registry item; the underlying primitive is also registered standalone as the headless message-scroller item.

Terminal window
navius add chat --to ./src/MyApp --namespace MyApp.Ui
<ZitsMessageScrollerProvider AutoScroll="true" DefaultScrollPosition="end">
<ZitsMessageScroller class="h-80 rounded-2xl border">
<ZitsMessageScrollerViewport class="px-4 py-3">
<ZitsMessageScrollerContent>
@foreach (var m in _messages)
{
<ZitsMessageScrollerItem @key="m.Id" MessageId="m.Id" ScrollAnchor="m.Anchor">
<ZitsMessage Align="@(m.Role == "user" ? "end" : "start")">
<ZitsMessageContent>@m.Text</ZitsMessageContent>
</ZitsMessage>
</ZitsMessageScrollerItem>
}
</ZitsMessageScrollerContent>
</ZitsMessageScrollerViewport>
<ZitsMessageScrollerButton />
</ZitsMessageScroller>
</ZitsMessageScrollerProvider>

ZitsMessageScrollerButton adds two Zits-only styling markers not forwarded to the Navius primitive:

Prop Type Default Description
Variant string "secondary" Zits-only marker (not forwarded to NaviusMessageScrollerButton); sets data-variant, used only to pick the local Tailwind class set.
Size string "icon-sm" Zits-only marker (not forwarded); sets data-size, same purpose.

Every other parameter (AutoScroll, DefaultScrollPosition, ScrollEdgeThreshold, ScrollMargin, ScrollPreviousItemPeek on the provider; PreserveScrollOnPrepend on the viewport; SpacerClass on the content; MessageId/ScrollAnchor on the item; Direction/ Behavior on the button) passes straight through to the matching Navius primitive, unchanged.

ZitsMessageScroller, ZitsMessageScrollerViewport, ZitsMessageScrollerContent, ZitsMessageScrollerItem, and ZitsMessageScrollerButton accept Attributes, merged via Cn.Merge with the caller’s classes applied last. ZitsMessageScrollerProvider renders no DOM and takes no Attributes.

Parameter Values
Button.Variant secondary (only value used by the shipped styling)
Button.Size icon-sm (only value used by the shipped styling)
Part Attribute Description
Message Scroller (frame) data-scrollable, data-autoscrolling Mirrored from the engine onto the frame as the positioning parent for the scroll button.
Viewport data-autoscrolling Hides the scrollbar thumb/track while the viewport programmatically follows the latest message.
Button data-active, data-direction data-active=false slides the button out and makes it inert (never a ghost focus stop) when the viewport can’t scroll that way; data-direction picks the top/bottom offset and flips the icon for start.

For programmatic control, inject the cascaded MessageScrollerContext (from Navius.Primitives.Components.MessageScroller) anywhere inside the provider: ScrollToMessageAsync, ScrollToStartAsync, ScrollToEndAsync, plus the ScrollableStart/ ScrollableEnd, CurrentAnchorId, and VisibleMessageIds readouts. Each row’s content-visibility: auto keeps off-screen rows out of render work while leaving them in the DOM for selection, copy, find-in-page, and assistive tech.