ScrollArea
A custom scrollable container with styled, OS-consistent scrollbars. Built on @base-ui/react/scroll-area. Supports both vertical and horizontal scrolling.
Anatomy
<ScrollArea> is the root. It wraps a viewport and injects a styled <ScrollBar> automatically. The internal ScrollBar can also be used standalone for a horizontal scrollbar.
Examples
Tag List
Message Log
Horizontal Scroll
Set w-max on the inner content and constrain the container width to enable horizontal scrolling.
Design Guidelines
Do
- Set an explicit height. Without a fixed height the scroll area collapses to fit its content and scrolling never activates.
- Use for overflow within a layout region. Sidebars, log panels, tag pickers, and message feeds are good candidates.
- Prefer scrollable containers over page scroll for panels. Isolating scroll to a region keeps the rest of the layout stable.
Don't
- Don't wrap the whole page. Page-level scrolling should use the native browser scroll, not a custom ScrollArea.
- Don't use for short lists. If all items fit without scrolling, the custom scrollbar is unnecessary visual noise.
- Don't forget keyboard access. The viewport is focusable — users should be able to scroll with arrow keys once it receives focus.
Developer Reference
Accessibility
- The viewport receives
focus-visiblering styles — keyboard users can focus it and scroll with arrow keys. - Custom scrollbars are hidden from the accessibility tree via
aria-hidden; native scroll semantics are preserved on the viewport. - Use
ScrollBardirectly withorientation="horizontal"when you need both axes independently styled.
Usage
import { ScrollArea } from "@/components/ui/scroll-area"
// Vertical scroll
<ScrollArea className="h-48 w-full rounded-lg border p-4">
{items.map((item) => (
<div key={item.id}>{item.label}</div>
))}
</ScrollArea>
// Horizontal scroll
<ScrollArea className="w-full rounded-lg border">
<div className="flex w-max gap-4 p-4">
{items.map((item) => (
<div key={item.id} className="w-32 shrink-0">{item.label}</div>
))}
</div>
</ScrollArea>