ScrollArea
A styled scroll container that keeps the platform's scrolling semantics.
@luc/uiExamples
Vertical scrolling
A region that scrolls with a scrollbar the design system owns: one width, thumb and corner on every platform, sitting beside the content instead of over the last row.
Instruments
24<Panel>
<PanelHeader>
<Text variant="label">Instruments</Text>
<Badge tone="neutral" size="sm">24</Badge>
</PanelHeader>
<ScrollArea className="h-64" viewportClassName="p-3">…</ScrollArea>
</Panel>Horizontal scrolling
`viewportClassName` is where the content's own padding and layout live. A wide table scrolls inside its box instead of widening the page — which is why this component sits under the DataTable. Shown with `type="always"` so the bar is visible in a screenshot; the default is `hover`.
<ScrollArea type="always" viewportClassName="p-3">
<Grid columns={12} gap="sm" className="w-[64rem]">…</Grid>
</ScrollArea>Both axes
A region that is too wide *and* too tall gets two scrollbars and the corner that keeps them from overlapping — the case a bare `overflow-auto` box gets subtly wrong on every platform.
<ScrollArea type="always" className="h-56" viewportClassName="p-3">
<Grid columns={8} gap="sm" className="w-[72rem]">…</Grid>
</ScrollArea>Scrollbar policy
`type` decides when the scrollbar is visible: `hover` keeps it present but quiet, `always` reserves it so the there-is-more cue is there without touching the region.
<ScrollArea type="hover" …>…</ScrollArea>
<ScrollArea type="always" …>…</ScrollArea>Long content
What makes a scroll region worth naming: content long enough to scroll, a last row that is reachable, and the region itself a tab stop so a keyboard reader can reach it without a pointer.
<ScrollArea className="h-48" viewportClassName="p-3">
<Stack gap="md">…</Stack>
</ScrollArea>Usage
import { ScrollArea } from "@luc/ui";<ScrollArea className="h-64" viewportClassName="p-3">
…
</ScrollArea>Give the region a definite height, and put the content's own padding on `viewportClassName` so it scrolls with the content.
Package setup — peer requirements, the stylesheet and the provider — is on installation.
API
Read from the published TypeScript API, so a prop that is not listed here does not exist and a default that is listed is the one the compiler sees.
| Export | Kind | Signature |
|---|---|---|
ScrollArea | 8 documented props | ({ className, viewportClassName, type, scrollbarSize, corner, children, ...props }: ScrollAreaProps) => import("@types/react").JSX.Element |
ScrollAreaRoot | 4 documented props | import("@types/react").ForwardRefExoticComponent<RadixScrollArea.ScrollAreaProps & import("@types/react").RefAttributes<HTMLDivElement>> |
ScrollArea props
Type: ScrollAreaProps
Values the compiler reads from the declaration.
| Prop | Type | Default | Notes |
|---|---|---|---|
asChild?inherited | boolean | undefined | — | |
children? | ReactNode | — | |
corner? | boolean | undefined | — | Render the corner filler between the two scrollbars. Default `true`. |
dir?inherited | Direction | undefined | — | |
scrollbarSize? | number | undefined | — | Fixed size for both scrollbars, in px. |
scrollHideDelay?inherited | number | undefined | — | |
type? | "auto" | "always" | "hover" | "scroll" | undefined | — | Scrollbar visibility policy. `hover` is the default — present, not loud. |
viewportClassName? | string | undefined | — | Viewport class. Height, padding and layout belong here. |
ScrollAreaRoot props
Type: ScrollAreaProps & RefAttributes<HTMLDivElement>
Values the compiler reads from the declaration.
| Prop | Type | Default | Notes |
|---|---|---|---|
asChild?inherited | boolean | undefined | — | |
dir?inherited | Direction | undefined | — | |
scrollHideDelay?inherited | number | undefined | — | |
type?inherited | "auto" | "always" | "hover" | "scroll" | undefined | — |
Guidelines
Do
- Give the region a definite size or a definite parent: a scroll area with no height constraint simply grows, and the reader gets the page's scrollbar instead of the component's.
- Pass the content's padding and layout through `viewportClassName`, so the padding scrolls with the content and the scrollbar does not land on top of it.
- Use it for a long list, a wide table or a code block inside a fixed-height panel — the cases where a platform scrollbar's width would shift the layout.
Don't
- Do not nest two scroll areas on the same axis: nested vertical scrolling traps a trackpad and a keyboard alike.
- Do not hide the scrollbar to make a region prettier — `type="hover"` keeps the cue, and `type="always"` is for a reader who has to know there is more.
- Do not use it as a page container: the page already scrolls, and a second scrollbar costs the reader their position in it.
Accessibility
The viewport is a tab stop with a visible focus ring, because a keyboard user cannot drag a scrollbar and the content has to be reachable without a pointer. The scrollbars themselves are decorative in the accessibility tree, so nothing is announced twice — and content below the fold is still in the DOM, so it is still read and still searchable.
Family
Layout
Space, rhythm and the surfaces a page is made of.
10 documented entries in this family. Open the family index
Related