Scroll Area
StableA scrollable region with quiet overlay scrollbars that match the theme — native scrolling underneath, consistent chrome on top.
Installation
$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/scroll-area.jsonUsage
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"<ScrollArea className="h-72 w-48 rounded-xl border">
{/* long content */}
</ScrollArea>Give the scroll area a fixed height (or width, for horizontal content) — it scrolls whatever overflows that box. Scrolling itself is native: wheel, trackpad momentum, touch, keyboard and scroll anchoring all behave exactly as they would on a plain overflow: auto element.
Examples
Horizontal
Content that should scroll sideways needs to be wider than the area (w-max on a flex row) and a horizontal scrollbar. Add <ScrollBar orientation="horizontal" /> as a child — the vertical one is always included and only appears when there's vertical overflow.
Both axes
Wide data — spreadsheets, timelines, large tables — can scroll in both directions. Sticky headers and first columns keep their bearings; the corner where scrollbars meet stays clear.
Fading edges
The root reports where content overflows through data-overflow-y-start and data-overflow-y-end (and -x- equivalents). Fade the edge only when there is more content in that direction — a cue that the region scrolls, which disappears once you reach the end.
<ScrollArea
className={cn(
"relative h-72",
"after:absolute after:inset-x-0 after:bottom-0 after:h-10 after:bg-linear-to-t after:from-card",
"after:opacity-0 after:transition-opacity data-overflow-y-end:after:opacity-100"
)}
>Scrollbars
Scrollbars overlay the content instead of taking space from it, so layouts don't shift between platforms. They stay hidden until you hover the area or scroll, then fade away; the thumb darkens on hover and can be dragged.
Guidelines
When to use
- For bounded regions inside a layout that scroll independently: menus, side panels, chat threads, lists in cards, code.
- When the platform's default scrollbar would clash with a dense, themed interface — especially thick, always-visible scrollbars on Windows.
When not to use
- For the page itself. Let the document scroll natively; nested scrolling for the main content makes people fight two scroll positions.
- To hide content that doesn't fit. If people must scroll a small box to find something important, give it more room.
- For horizontal scrolling of primary content on desktop. People miss sideways overflow; prefer wrapping or pagination.
Show that it scrolls
Overlay scrollbars are hidden at rest, so the region itself has to suggest that there's more. Cut content mid-item at the edge, fade the overflowing edge, or show a count.
Changelog
v1.4.0 — Tokens v2
v1.3.2 — Fixes
v1.3.1 — Dialog focus
Changelog
v1.4.0 — Tokens v2
v1.3.2 — Fixes
One direction at a time
Prefer a single scroll axis per region. Two-axis scrolling is right for grids of data, where people expect it; elsewhere it makes content feel lost.
Accessibility
- Keyboard scrolling. The viewport becomes a tab stop only while its content overflows. Once focused, ↑ ↓ ← →, Page Up, Page Down, Home, End and Space scroll it natively, and a 2px inset focus ring shows where you are.
- Name the region. A focusable scroll region with no name is announced as nothing in particular. When the content is meaningful on its own, wrap it in a labelled landmark (
<section aria-label="Release history">) or give it a visible heading. - Scrollbars are pointer affordances. Screen reader and keyboard users scroll the viewport directly; the custom scrollbars are hidden from assistive technology like native ones.
- Reduced motion. Scrollbar fades are instant with
prefers-reduced-motion.
API reference
ScrollArea
The scroll container: root, viewport, a vertical ScrollBar and the corner. className applies to the root; children render inside the viewport. Accepts every prop of Base UI ScrollArea.Root.
ScrollBar
A scrollbar and its thumb. Add one with orientation="horizontal" for sideways scrolling. Accepts every prop of Base UI ScrollArea.Scrollbar.
Data attributes
Set on the root; use them to style edges and states.