Skip to content
Cynosurev0.5.0
SearchStorybookGitHubnpmColour mode: System. Switch to Light

Components / Structure

ScrollArea

A box that fills its parent and scrolls what does not fit. It can follow the end while content arrives and hold an overlay, such as a "3 new" pill, that stays put while the content scrolls.

Follow keeps the end in view

When to useLink to "When to use"

  • The scrolling middle of an app shell: a panel between a header and a status bar that scrolls instead of making the page longer.
  • Logs, chats and activity streams: follow keeps the latest in view, onAtBottomChange tells you when someone scrolled up to read, and overlay holds the way back.

Use something else when:

  • the content is a long list of rows people pick from: VirtualList (it renders only the rows in view, and has follow and overlay too)
  • a box only needs to clip or scroll and has nothing else to do: overflow on Stack, Inline or Grid
  • the content is code: CodeBlock scrolls its own

UsageLink to "Usage"

tsx
Copy
import { ScrollArea, type ScrollAreaHandle } from "@arshad-shah/cynosure";

const area = useRef<ScrollAreaHandle>(null);
const [atBottom, setAtBottom] = useState(true);

<Stack fill gap="0">
  <Toolbar label="Activity">…</Toolbar>
  <ScrollArea
    label="Activity"
    follow
    handle={area}
    onAtBottomChange={setAtBottom}
    overlay={!atBottom && <Button onClick={() => area.current?.scrollToEnd()}>Latest</Button>}
  >
    {events}
  </ScrollArea>
</Stack>

The area is a flex share and 100% of its parent both ways, with a minimum size of 0: in a flex column it takes what the header and footer leave. In a parent with no height of its own it is as tall as its content.

Fills its parent

Following the endLink to "Following the end"

With follow, the end stays in view while content is added. Scrolling up lets go (and onAtBottomChange(false) is called); scrolling back to the end, or handle.scrollToEnd(), follows again (onAtBottomChange(true)). Without follow, new content arrives below and onAtBottomChange still reports where the view is.

Scrolling back to the end resumes

OverlayLink to "Overlay"

overlay sits over the area at the bottom centre and does not scroll. It is outside the named region. The area is an OverlayFrame, which places it.

Overlay stays put

Scrolling from outsideLink to "Scrolling from outside"

handle.scrollToEnd() goes to the end. handle.scrollIntoView(element, { align }) scrolls only the area, never the page around it, so element is in view: nearest (the least scrolling, the default), start or center. It works in right-to-left text.

Scroll into view moves only the area

DirectionLink to "Direction"

direction is vertical (default), horizontal or both; the other axis is clipped.

Direction picks the axes

PropsLink to "Props"

Every prop of ScrollArea
PropWhat it doesTypeDefault
label
Accessible name. With it the area is a named region, and a tab stop whenever there is something to scroll, so a keyboard can scroll it. Give one unless the content has its own focusable elements (links, buttons, a list).string
direction
Which way it scrolls; content is clipped the other way. Default "vertical"."horizontal" | "vertical" | "both""vertical"
follow
Keep the end in view while content is added (a log, a chat, an activity stream). Scrolling up lets go; scrolling back to the end, or handle.scrollToEnd(), follows again.boolean
onScroll
Called on every scroll of the area, with the scroll event of its scrolling element.((event: UIEvent<HTMLDivElement>) => void)
onAtBottomChange
Called with false when the view leaves the end (the bottom) and true when it is back, from scrolling or from content growing past it. It starts true. Use it to show a "jump to latest" control.((atBottom: boolean) => void)
overlay
Content pinned over the area at the bottom centre that does not scroll: a "3 new" pill, a "jump to latest" button. It is outside the named region.ReactNode
handle
scrollToEnd and scrollIntoView, for scrolling from outside.Ref<ScrollAreaHandle>
className
Added to the root element, for layout.string
style
Layout only (e.g. maxBlockSize). Restyle through --cy-scroll-area-* tokens.CSSProperties
children
What scrolls.ReactNode
data-testid
Set on the root element, for tests.string

Generated from the type definitions in @arshad-shah/cynosure 0.5.0.

Behaviour and accessibilityLink to "Behaviour and accessibility"

  • Name and focus: with label, the scrolling element is a region of that name, and a tab stop while there is something to scroll. With nothing to scroll it is not a tab stop. Without label it is neither, so leave label out only when the content has its own focusable elements: focusing them scrolls the area.
Behaviour and accessibility, table 1
Key (region focused)Does
Page Up / DownScroll a screenful
Home / EndTop / end
Keyboard scrolls the region
  • Shadow roots: following and the overlay work inside a shadow root.

TouchLink to "Touch"

A finger pans the area with the browser's own scrolling; nothing intercepts it. Controls in the overlay keep their own hit areas.

ThemingLink to "Theming"

No tokens of its own: the overlay's place comes from OverlayFrame's --cy-overlay-frame-offset (space between the bottom edge and the overlay, default --cy-space-4) and --cy-overlay-frame-layer. The area itself draws nothing: give the parent its background and border.

VirtualList · Stack · SplitPane · CodeBlock

These examples run Cynosure 0.5.0 (commit 50ce30b), the version on npm.

Getting started
Theming
Icons
Accessibility
Code editors and highlighters
Using Cynosure without React
Build-tool adapters
Components
Accordion
AppShell
AspectRatio
Breadcrumbs
Calendar
Card
Container
Disclosure
Divider
DocumentViewport
Grid
Inline
PageHeader
PanZoomCanvas
ScrollArea
SideNav
SortableList
SplitPane
Stack
StatusBar
Tabs
TitleBar
Toolbar
Tree
VirtualList
ZoomControls
Announcer
Avatar
Badge
CodeBlock
DescriptionList
EmptyState
ErrorBoundary
Field
Heading
IconTile
Image
Kbd
List
LoadingDots
Notice
ProgressBar
Prose
Skeleton
Stat
Steps
Table
Text
Button
Checkbox
Checklist
ColorField
ColorSwatches
Combobox
CompareSlider
CopyButton
DateField
FileDrop
IconButton
KeyValueEditor
Link
NumberField
Pagination
PathField
RadioGroup
SearchField
SegmentedControl
Select
SelectionFrame
SignaturePad
Slider
Switch
TagInput
TextArea
TextField
ThemeToggle
TimeField
ButtonBase
ChoiceBase
DialogBase
FileInputBase
HiddenValue
Icon
InputBase
LinkBase
LiveRegion
TextAreaBase
ChatMessage
Choice
DataGrid
DeviceFrame
HighlightedText
HotkeyField
ListRow
OptionList
OverlayFrame
QRCode
RowActions
Section
SiteFooter
SiteHeader
SkipLink
SortButton
VisuallyHidden
CommandPalette
ContextMenu
Dialog
FloatingToolbar
HoverCard
Menu
Menubar
Popover
ProgressOverlay
Sheet
Toast
Tooltip
Changelog