ScrollArea
A scrollable container that wraps arbitrary content. Control which axis scrolls via dir, clamp the container height with height or maxHeight, and tune when the scrollbar thumb appears with type. The scrollbar thickness is configurable via scrollbarSize.
Vertical scroll
The default — dir="y" — scrolls vertically. Set a fixed height to constrain the container.
Item 1 — scroll down to see more
Item 2 — scroll down to see more
Item 3 — scroll down to see more
Item 4 — scroll down to see more
Item 5 — scroll down to see more
Item 6 — scroll down to see more
Item 7 — scroll down to see more
Item 8 — scroll down to see more
Item 9 — scroll down to see more
Item 10 — scroll down to see more
<ScrollArea height={160}>
<Stack gap="xs">
{items.map((item) => <Text>{item}</Text>)}
</Stack>
</ScrollArea>Max height
Use maxHeight instead of height when the container should shrink to fit short content but scroll once it overflows.
Short list item 1
Short list item 2
Short list item 3
{/* Container shrinks to fit 3 items, scrolls if content grows past 200px */}
<ScrollArea maxHeight={200}>
<Stack gap="xs">
<Text>Item 1</Text>
<Text>Item 2</Text>
<Text>Item 3</Text>
</Stack>
</ScrollArea>Horizontal scroll
Set dir="x" for horizontal-only scrolling. Useful for wide tables or code blocks that shouldn't wrap.
Card 1
Card 2
Card 3
Card 4
Card 5
Card 6
Card 7
Card 8
Card 9
Card 10
Card 11
Card 12
<ScrollArea dir="x">
<div style={{ display: 'flex', gap: 'var(--dv-spacing-md)', whiteSpace: 'nowrap' }}>
{cards.map((card) => <Card key={card.id}>{card.title}</Card>)}
</div>
</ScrollArea>Both axes
Use dir="xy" to allow scrolling in both directions — handy for data grids or large canvas areas.
Row 1 — this content is wider than the container, scroll right to see it all
Row 2 — this content is wider than the container, scroll right to see it all
Row 3 — this content is wider than the container, scroll right to see it all
Row 4 — this content is wider than the container, scroll right to see it all
Row 5 — this content is wider than the container, scroll right to see it all
Row 6 — this content is wider than the container, scroll right to see it all
Row 7 — this content is wider than the container, scroll right to see it all
Row 8 — this content is wider than the container, scroll right to see it all
<ScrollArea dir="xy" height={160}>
<div style={{ width: 600 }}>
{rows.map((row) => <Text>{row}</Text>)}
</div>
</ScrollArea>Scrollbar visibility
The type prop controls when the scrollbar thumb is visible. The default 'auto' shows it only on hover or focus. Use 'always' to keep it permanently visible, or 'never' to hide it entirely (content is still scrollable).
type="auto"
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
type="always"
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
type="never"
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
{/* show on hover/focus (default) */}
<ScrollArea type="auto" height={200}>…</ScrollArea>
{/* always visible */}
<ScrollArea type="always" height={200}>…</ScrollArea>
{/* hidden but still scrollable */}
<ScrollArea type="never" height={200}>…</ScrollArea>Scrollbar size
Adjust the scrollbar thumb thickness with scrollbarSize (pixels). Default is 6.
scrollbarSize=3
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
scrollbarSize=6
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
scrollbarSize=10
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
<ScrollArea scrollbarSize={3} height={200}>thin thumb</ScrollArea>
<ScrollArea scrollbarSize={6} height={200}>default</ScrollArea>
<ScrollArea scrollbarSize={10} height={200}>thick thumb</ScrollArea>Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | 'auto'|'always'|'never' | 'auto' | When to show the scrollbar thumb |
dir | 'x'|'y'|'xy' | 'y' | Which axis(es) scroll |
height | number|string | — | Fixed height of the scroll container (number = px) |
maxHeight | number|string | — | Container grows to fit content up to this height, then scrolls |
scrollbarSize | number | 6 | Scrollbar thumb thickness in pixels |
class | string | — | Additional CSS class names |
style | string|object | — | Inline styles merged with generated CSS custom properties |