ScrollContainer
Wrap content in a flex column that scrolls to the end or to a target element when its inputs change.
ScrollContainer is a client component ("use client") that renders a full-width flex-column Box and runs a scroll effect whenever watch, scrollToId or the scroll options change. It suits chat logs, activity feeds and in-page section navigation.
Import#
import { ScrollContainer } from "@reactberry/system/blocks";
Usage#
import { ScrollContainer } from "@reactberry/system/blocks";
import { Box, Text } from "@reactberry/system/elements";
type Message = { id: string; body: string };
export default function ChatLog({ messages }: { messages: Message[] }) {
return (
<ScrollContainer watch={messages.length} scrollBlock="end" height="24rem" overflowY="auto" gap="xs">
{messages.map((message) => (
<Box key={message.id} skin="surface" shape="rounded" p="s">
<Text>{message.body}</Text>
</Box>
))}
</ScrollContainer>
);
}
Examples#
Follow new messages#
Change watch to scroll to the end, for example after sending a message. Here watch stays undefined until the first send, so nothing scrolls on mount.
Offset for a sticky header#
scrollOffset subtracts pixels from the target position, so a section isn't hidden behind a sticky header.
API#
ScrollContainer#
| Prop | Type | Default | Description |
|---|---|---|---|
watch | any | — | When defined, each change scrolls the end marker into view. |
scrollToId | string | — | Id of an element to scroll to. Takes priority over |
onScrollStart | (() => void) | — | Called each time the effect runs with a |
scrollOffset | number | 0 | Pixels subtracted from the target position when scrolling to |
scrollBehavior | "auto""instant""smooth" | "smooth" | Scroll behaviour for the |
scrollBlock | "start""end""center""nearest" | "start" | Vertical alignment of the end marker for the |
All other BoxProps are spread onto the root after width="100%", flex="auto", display="flex" and flexDirection="column".
Notes#
- The root does not set
overflow. Give it a constrained height andoverflowY="auto"(or place it in a parent that does) or there is nothing to scroll. scrollToIdscrolls the container itself usingscrollTowith smooth behaviour;scrollBehaviorandscrollBlockare not used in this path. The element is looked up withdocument.getElementById, so the id must be unique on the page.- The
watchpath callsscrollIntoViewon the end marker, which can also scroll ancestor containers and the window. UsescrollBlock="end"to align the marker to the bottom edge. - Because the effect runs on mount, a defined
watchscrolls to the end on first render. onScrollStartis an effect dependency. An inline function changes identity every render and re-triggers the scroll; pass a stable callback (for example fromuseCallback).- The component appends an empty
divand aBoxwithpt="1rem"after the children, adding 1rem of space at the end. - The code also contains a fallback that sets
scrollMarginTopand callsscrollIntoViewforscrollToId, and a fallback that scrolls toscrollHeightforwatch. Neither runs in practice, because the container ref is always set and the earlierwatchcheck covers every defined value.