Scroll Area
A scrollable region with a scrollbar that looks the same on every platform.
Installation
npx shadcn@latest add https://qure-ui.qure.ai/r/scroll-area.jsonUsage
import { ScrollArea } from '@/components/ui/scroll-area'<ScrollArea className="h-64">
{/* … */}
</ScrollArea>The height belongs to the caller. ScrollArea is overflow: hidden with a viewport filling it,
so a ScrollArea with no height on it, or on an ancestor, scrolls nothing and shows nothing wrong
— it simply grows.
This is a cosmetic scrollbar over a real one: the viewport still scrolls natively, so wheel, trackpad, touch, keyboard and find-in-page all behave. It is the wrong choice for the page itself — hijacking the document scrollbar breaks the browser's own scroll position restore — and the right choice for a panel inside a layout that has to look identical on Windows and macOS.
Composition
<ScrollArea> {/* clips, and positions the scrollbars */}
<Viewport> {/* the element that actually scrolls */}
<Content> {/* measured, so the thumb knows its size */}
<ScrollBar /> {/* one per axis, rendered by orientation */}
<Corner /> {/* only when both axes are present */}
</ScrollArea>Our ScrollArea renders all of that for you and takes an orientation of vertical,
horizontal or both. ScrollBar is exported separately for the rare case where a scrollbar
needs to be placed by hand.
Vertical
The default. A worklist that has to stay a fixed height inside a column of other panels.
Horizontal
Series thumbnails. Note the w-max on the inner row — without it, the flex row shrinks to fit
the viewport and there is nothing to scroll.
Both axes
A measurements table wider than the panel holding it. orientation="both" adds the corner
between the two bars, so they do not overlap at the intersection.
Inside a card
Put the scroll around the report body, not around the card: the title and the sign-off row should stay put while the text moves. The padding goes on the content inside the scroll area, so the scrollbar sits against the card's edge rather than floating in the middle of the inset.
Nothing here changes the tab order. A scrollable region with no focusable child is still
unreachable by keyboard — give the viewport tabIndex={0} and an aria-label in that case,
which is the one situation this component cannot decide for you.
API Reference
Prop
Type
The root also forwards everything
Base UI's ScrollArea accepts, including
overflowEdgeThreshold for fading the edges of a scrolled region.