Skip to content
Layout Primitives

Scroll Area

A scrolling frame with a scrollbar the theme draws, instead of the one the operating system draws. Same overflow behaviour, a bar that matches the rest of the set.

Import

import {
  ScrollArea,
  ScrollBar,
} from "@mikenotthepope/substrateui"

It needs a height

The root is overflow-hidden and takes no size of its own. Without a height or max-height it grows to fit its content and never scrolls — which is the one way this component silently does nothing. Put the constraint on the ScrollArea itself, not on a wrapper.

Scrolling sideways

Both bars are already there — Base UI drops the one for an axis that doesn't overflow, so you get whichever the content calls for without asking. What you do need is inner content genuinely wider than the frame: w-max on the row below, so it sizes to its children instead of to the container.

Direction

The vertical bar's border uses border-s, so it sits against the inline-end edge and moves to the left of the frame in RTL, where the browser puts the scrollbar. Nothing to configure.

Horizontal scrolling starts from the inline-start edge, so in RTL the content begins at the right and scrolls leftward. If you set a scroll position programmatically, remember scrollLeftis negative or reversed in RTL depending on the browser — read it, don't assume it.

Accessibility

The viewport is a real scroll container, so the keyboard works as it does anywhere: arrows, Page Up and Page Down, Home and End — once focus is inside. Content that is only reachable by scrolling must contain something focusable, or a keyboard user cannot get to it.

The custom bar replaces the platform one, which is the whole point and also the cost: it does not honour a user's always-visible scrollbar setting, and the thumb is drawn in bg-border. Do not use a scrolling frame as the only cue that there is more content — a fading edge or a count says so without depending on the bar being noticed.

The thumb is 10px wide, under the 24px pointer-target guideline. That is acceptable because the mouse wheel, the trackpad, and the keyboard all scroll without touching it; it would not be if dragging the thumb were the only way to move.

API Reference

Built on Base UI's ScrollArea: the root, viewport, both scrollbars, their thumbs, and the corner are assembled here so a caller passes one element. Everything you pass goes to the root — see the Base UI scroll-area docs for that surface.

PropTypeDefaultDescription
classNamestringWhere the height goes. The root is overflow-hidden with no size of its own, so without a height or max-height nothing scrolls.

ScrollBar

PropTypeDefaultDescription
orientation"vertical" | "horizontal""vertical"Which scrollbar to draw. ScrollArea renders both, so you only need this when composing Base UI's primitives yourself.