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.

overview
install
configure
deploy

Import#

typescript
import { ScrollContainer } from "@reactberry/system/blocks";

Usage#

typescript
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.

Hi, is the release ready?
Almost, one test left.
Ping me when it's green.

Offset for a sticky header#

scrollOffset subtracts pixels from the target position, so a section isn't hidden behind a sticky header.

Introduction
Pricing
Support

API#

ScrollContainer#

PropTypeDefaultDescription
watch
any
—

When defined, each change scrolls the end marker into view.

scrollToId
string
—

Id of an element to scroll to. Takes priority over watch.

onScrollStart
(() => void)
—

Called each time the effect runs with a scrollToId.

scrollOffset
number
0

Pixels subtracted from the target position when scrolling to scrollToId.

scrollBehavior
"auto""instant""smooth"
"smooth"

Scroll behaviour for the watch scroll.

scrollBlock
"start""end""center""nearest"
"start"

Vertical alignment of the end marker for the watch scroll.

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 and overflowY="auto" (or place it in a parent that does) or there is nothing to scroll.
  • scrollToId scrolls the container itself using scrollTo with smooth behaviour; scrollBehavior and scrollBlock are not used in this path. The element is looked up with document.getElementById, so the id must be unique on the page.
  • The watch path calls scrollIntoView on the end marker, which can also scroll ancestor containers and the window. Use scrollBlock="end" to align the marker to the bottom edge.
  • Because the effect runs on mount, a defined watch scrolls to the end on first render.
  • onScrollStart is an effect dependency. An inline function changes identity every render and re-triggers the scroll; pass a stable callback (for example from useCallback).
  • The component appends an empty div and a Box with pt="1rem" after the children, adding 1rem of space at the end.
  • The code also contains a fallback that sets scrollMarginTop and calls scrollIntoView for scrollToId, and a fallback that scrolls to scrollHeight for watch. Neither runs in practice, because the container ref is always set and the earlier watch check covers every defined value.