ScrollArea
A scroll container with the house scrollbar instead of the browser’s: compose ScrollAreaViewport › ScrollAreaContent, then one ScrollAreaScrollbar per axis and a ScrollAreaCorner where two of them meet. It can only scroll inside a bounded parent, so give the wrapper a height or a width — with nothing to overflow, the content simply grows.
Usage
Examples
Horizontal
orientation='horizontal' puts the bar along the bottom edge, for a row that runs past its container rather than a column that runs below it.
Virtualized
ScrollAreaViewport forwards its ref to the scrolling element itself, so a row
virtualiser such as @tanstack/react-virtual can track scroll position inside the
house scrollbar instead of falling back to a bare overflow-y: auto div. Ten thousand
rows here, a few dozen in the DOM.
Two things this story is deliberately showing:
useVirtualizeris called in the same component that declares the ref. Called from a component rendered inside the viewport,getScrollElement()runs once — before React has attached the host element's ref — returnsnull, and nothing ever re-runs it, so the list stays permanently empty.ScrollAreaContentgetsh-auto min-h-fullso it grows with the virtual sizer. The scrollbar is only rendered while the viewport reportsdata-overflow-y, and that flag is recomputed from aResizeObserverwatching the content box — which at its defaulth-fullstays the height of the viewport. A list that grows after mount therefore keeps its scrollbar hidden until something else forces a remeasure.