Scroll Area

Scroll container with thin scrollbars that stay inside its frame.

Installation

pnpm dlx shadcn@latest add @paneui/scroll-area

Usage

Composition

Examples

Both Axes

Use for grids and schedules wider and taller than their frame.

Sticky Header

Track placement

Each track sits inside the frame, scrollbarInset (4px) from its edges, so it never runs past a rounded corner. Where a corner's radius would still clip the thumb's cap, that end moves in until the cap clears the curve by 1px (7px on an 18px corner). Where both bars show, each stops 8px short of the other. The thumb always covers the whole scroll range.

Visibility

The 6px bars overlay the content and take no width. They show while the pointer is over the area or it is scrolling, and fade out 300ms after. The hit area is 4px wider on each side than the track.

Height

Give the root a fixed height (h-72), or leave it auto and put max-h-* on viewportClassName. A max-h-* on the root alone never scrolls.

Accessibility

The viewport joins the tab order only while it can scroll, so keyboard users can scroll it with the arrow keys; it shows the focus ring inset.

Overlays

The root publishes --scroll-area-inset, --scroll-area-radius-top and --scroll-area-radius-bottom, so content inside can follow the frame's edge. Scroll Progress uses them for its sticky bar.

API Reference

Built on the Scroll Area primitive. Only the props this library adds or changes are listed; everything else passes through.

ScrollArea

Root and viewport in one, with its own scrollbars. Also accepts every prop of ScrollArea.Root.

orientation= "vertical"
"vertical" | "horizontal" | "both"
Which scrollbars to show. The other axis is clipped.
scrollbarInset= 4
number
Gap in px between each track and the frame edges.
scrollbarMargin
{ top?: number; bottom?: number; start?: number; end?: number }
Extra px on one side, added to the inset, such as a sticky header's height on top.
fade= false
boolean
Fades content over 24px at an edge while there is more to scroll there.
viewportRef
Ref<HTMLDivElement>
The element that scrolls, for scroll listeners, virtualizers and scrollTo.
viewportClassName
string
Classes for the element that scrolls.
viewportProps
ComponentProps<"div">
Props for the element that scrolls, such as role, aria-*, onScroll or style.
PropTypeDefaultDescription
orientation"vertical" | "horizontal" | "both""vertical"Which scrollbars to show. The other axis is clipped.
scrollbarInsetnumber4Gap in px between each track and the frame edges.
scrollbarMargin{ top?: number; bottom?: number; start?: number; end?: number }—Extra px on one side, added to the inset, such as a sticky header's height on top.
fadebooleanfalseFades content over 24px at an edge while there is more to scroll there.
viewportRefRef<HTMLDivElement>—The element that scrolls, for scroll listeners, virtualizers and scrollTo.
viewportClassNamestring—Classes for the element that scrolls.
viewportPropsComponentProps<"div">—Props for the element that scrolls, such as role, aria-*, onScroll or style.
data-scrolling
Present while the area is scrolling.
data-has-overflow-x / -y
Present while the content overflows on that axis.
data-overflow-y-start / -end
Present while there is more to scroll towards that edge (also -x-).
Data attributeDescription
data-scrollingPresent while the area is scrolling.
data-has-overflow-x / -yPresent while the content overflows on that axis.
data-overflow-y-start / -endPresent while there is more to scroll towards that edge (also -x-).

ScrollBar

One overlay scrollbar, placed from the root's inset variables. ScrollArea renders its own; use this only when composing the primitive parts yourself. Also accepts every prop of ScrollArea.Scrollbar.

orientation= "vertical"
"vertical" | "horizontal"
Axis the bar scrolls.
PropTypeDefaultDescription
orientation"vertical" | "horizontal""vertical"Axis the bar scrolls.
data-hovering
Present while the pointer is over the area.
Data attributeDescription
data-hoveringPresent while the pointer is over the area.