Components
Scroll Area
Augments native scroll with a slim, themed scrollbar that appears on interaction — keeping overflowing content contained without the browser's default chrome.
Basic usage
Give the ScrollArea a fixed height so taller content scrolls vertically inside it — without one there is nothing to scroll. A vertical ScrollBar and the corner are rendered for you; the viewport inherits the root's rounding, so a rounded-lg border on the root clips the content to match.
Horizontal scrolling
Add a <ScrollBar orientation="horizontal" /> and a fixed width to scroll a row sideways.
Scrollbar visibility
The scrollbar appears on hover by default and fades out again after scrollHideDelay. Set type="always" where the fact that a region scrolls at all is the information — a short list inside a dialog, a log pane — so nothing hides the only cue that there is more below.
Props
ScrollArea
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Tailwind classes merged onto the root. Set a fixed height/width here (e.g. h-48 w-72) to enable scrolling. |
childrenrequired | React.ReactNode | — | Content rendered inside the scrollable viewport. |
type | 'auto' | 'always' | 'scroll' | 'hover' | 'hover' | When the scrollbar is visible — auto: whenever the content overflows; always: permanently; scroll: only while scrolling; hover: only while hovering or scrolling. |
scrollHideDelay | number | 600 | Milliseconds before the scrollbar hides again. Only applies to the hover and scroll types. |
dir | 'ltr' | 'rtl' | — | Reading direction; flips the scrollbar to the correct side. |
...rest | ScrollArea.Root props | — | All Radix ScrollArea Root props are forwarded to the root, which carries data-slot="scroll-area". The viewport is rendered internally, so viewport-only props such as nonce cannot be passed through here. |
ScrollBar
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | 'vertical' | 'horizontal' | 'vertical' | Axis the scrollbar controls. A vertical bar is already rendered by ScrollArea — add a ScrollBar yourself only for the horizontal axis. |
forceMount | true | — | Keeps the scrollbar mounted so an external animation library can drive its enter/exit. Radix types it as the literal true — omit it rather than passing false. |
className | string | — | Merged onto the 10 px track. The thumb inside is bg-control-edge and is not separately exposed. |
...rest | ScrollAreaScrollbar props | — | All Radix ScrollAreaScrollbar props are forwarded; the element carries data-slot="scroll-area-scrollbar". |
Accessibility
- Once the viewport has focus, Arrow, Page Up/Down, and Home/End scroll it natively. Radix does not put a
tabIndexon the viewport, so whether it lands in the tab order is up to the browser's keyboard-focusable-scroller behaviour — keep focusable content (links, buttons) inside a long region so keyboard users can always reach the bottom of it. - The viewport carries a
focus-visiblering in the gold--ringtoken, offset against--background, so the state is styled wherever a browser does expose it. - The thumb is painted with
--control-edge, not--border. It is a draggable control and a position indicator, so it owes WCAG 1.4.11 3:1. The role token is what letsdata-contrast="high"pay that in one place: 3.83:1 in light, 4.94:1 in dark. On the default palette--control-edgeresolves to--input: the same #EDEEF1 step as border in light (1.16:1), but #434A5A in dark (2.00:1) where border sits at 1.10:1 — a known gap of that palette. - Built on Radix ScrollArea, which preserves native scroll semantics and assistive-technology behavior rather than replacing scrolling with JavaScript.
- Touch and trackpad gestures continue to work normally — the custom scrollbar is purely visual and never blocks pointer or wheel input.