Augments native scroll functionality for custom, cross-browser styling.
<ScrollArea.root
id="scroll-area-primitive"
class="DemoScrollAreaRoot"
type="always"
>
<ScrollArea.viewport class="DemoScrollAreaViewport">
<div style="padding: 15px 20px;">
<div class="DemoText">Tags</div>
<div class="DemoTag">v1.2.0-beta.50</div>
<div class="DemoTag">v1.2.0-beta.49</div>
<div class="DemoTag">v1.2.0-beta.48</div>
<div class="DemoTag">v1.2.0-beta.47</div>
<div class="DemoTag">v1.2.0-beta.46</div>
<div class="DemoTag">v1.2.0-beta.45</div>
<div class="DemoTag">v1.2.0-beta.44</div>
<div class="DemoTag">v1.2.0-beta.43</div>
<div class="DemoTag">v1.2.0-beta.42</div>
<div class="DemoTag">v1.2.0-beta.41</div>
<div class="DemoTag">v1.2.0-beta.40</div>
<div class="DemoTag">v1.2.0-beta.39</div>
<div class="DemoTag">v1.2.0-beta.38</div>
<div class="DemoTag">v1.2.0-beta.37</div>
<div class="DemoTag">v1.2.0-beta.36</div>
<div class="DemoTag">v1.2.0-beta.35</div>
<div class="DemoTag">v1.2.0-beta.34</div>
<div class="DemoTag">v1.2.0-beta.33</div>
<div class="DemoTag">v1.2.0-beta.32</div>
<div class="DemoTag">v1.2.0-beta.31</div>
<div class="DemoTag">v1.2.0-beta.30</div>
<div class="DemoTag">v1.2.0-beta.29</div>
<div class="DemoTag">v1.2.0-beta.28</div>
<div class="DemoTag">v1.2.0-beta.27</div>
<div class="DemoTag">v1.2.0-beta.26</div>
<div class="DemoTag">v1.2.0-beta.25</div>
<div class="DemoTag">v1.2.0-beta.24</div>
<div class="DemoTag">v1.2.0-beta.23</div>
<div class="DemoTag">v1.2.0-beta.22</div>
<div class="DemoTag">v1.2.0-beta.21</div>
<div class="DemoTag">v1.2.0-beta.20</div>
<div class="DemoTag">v1.2.0-beta.19</div>
<div class="DemoTag">v1.2.0-beta.18</div>
<div class="DemoTag">v1.2.0-beta.17</div>
<div class="DemoTag">v1.2.0-beta.16</div>
<div class="DemoTag">v1.2.0-beta.15</div>
<div class="DemoTag">v1.2.0-beta.14</div>
<div class="DemoTag">v1.2.0-beta.13</div>
<div class="DemoTag">v1.2.0-beta.12</div>
<div class="DemoTag">v1.2.0-beta.11</div>
<div class="DemoTag">v1.2.0-beta.10</div>
<div class="DemoTag">v1.2.0-beta.9</div>
<div class="DemoTag">v1.2.0-beta.8</div>
<div class="DemoTag">v1.2.0-beta.7</div>
<div class="DemoTag">v1.2.0-beta.6</div>
<div class="DemoTag">v1.2.0-beta.5</div>
<div class="DemoTag">v1.2.0-beta.4</div>
<div class="DemoTag">v1.2.0-beta.3</div>
<div class="DemoTag">v1.2.0-beta.2</div>
<div class="DemoTag">v1.2.0-beta.1</div>
</div>
</ScrollArea.viewport>
<ScrollArea.scrollbar
class="DemoScrollAreaScrollbar"
orientation="vertical"
>
<ScrollArea.thumb class="DemoScrollAreaThumb" />
</ScrollArea.scrollbar>
<ScrollArea.scrollbar
class="DemoScrollAreaScrollbar"
orientation="horizontal"
>
<ScrollArea.thumb class="DemoScrollAreaThumb" />
</ScrollArea.scrollbar>
<ScrollArea.corner class="DemoScrollAreaCorner" />
</ScrollArea.root>
.DemoScrollAreaRoot {
width: 200px;
height: 225px;
border-radius: 4px;
overflow: hidden;
box-shadow: 0 2px 10px var(--black-a4);
background-color: white;
--scrollbar-size: 10px;
}
.DemoScrollAreaViewport {
width: 100%;
height: 100%;
border-radius: inherit;
}
.DemoScrollAreaScrollbar {
display: flex;
/* ensures no selection */
user-select: none;
/* disable browser handling of all panning and zooming gestures on touch devices */
touch-action: none;
padding: 2px;
background: var(--black-a3);
transition: background 160ms ease-out;
}
.DemoScrollAreaScrollbar:hover {
background: var(--black-a5);
}
.DemoScrollAreaScrollbar[data-orientation="vertical"] {
width: var(--scrollbar-size);
}
.DemoScrollAreaScrollbar[data-orientation="horizontal"] {
flex-direction: column;
height: var(--scrollbar-size);
}
.DemoScrollAreaThumb {
flex: 1;
background: var(--mauve-10);
border-radius: var(--scrollbar-size);
position: relative;
}
/* increase target size for touch devices https://www.w3.org/WAI/WCAG21/Understanding/target-size.html */
.DemoScrollAreaThumb::before {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
height: 100%;
min-width: 44px;
min-height: 44px;
}
.DemoScrollAreaCorner {
background: var(--black-a5);
}
.DemoText {
color: var(--violet-11);
font-size: 15px;
line-height: 18px;
font-weight: 500;
}
.DemoTag {
color: var(--mauve-12);
font-size: 13px;
line-height: 18px;
margin-top: 10px;
border-top: 1px solid var(--mauve-6);
padding-top: 10px;
}
Anatomy
Import all parts and piece them together.
<ScrollArea.root id="…">
<ScrollArea.viewport>…</ScrollArea.viewport>
<ScrollArea.scrollbar orientation="vertical">
<ScrollArea.thumb />
</ScrollArea.scrollbar>
<ScrollArea.scrollbar orientation="horizontal">
<ScrollArea.thumb />
</ScrollArea.scrollbar>
<ScrollArea.corner />
</ScrollArea.root>
Anatomy
-
Root - Contains all the parts of a scroll area.
-
Viewport - The scrollable viewport area.
-
Scrollbar - Custom scrollbar for vertical or horizontal scrolling.
-
Thumb - The draggable thumb inside a scrollbar.
-
Corner - Where vertical and horizontal scrollbars meet.
API Reference
Root
Contains all the parts of a scroll area.
The type attribute describes scrollbar visibility, similar to macOS scrollbar preferences: "auto" shows scrollbars when content overflows; "always" keeps them visible; "scroll" shows them while scrolling; "hover" shows them while scrolling or hovering the scroll area (default).
When type is "scroll" or "hover", scroll_hide_delay sets how long scrollbars stay visible after interaction (milliseconds, default 600).
| Prop | Type | Default | Description |
|---|---|---|---|
dir
|
string
|
"ltr"
|
|
id
|
string
|
—
|
|
scroll_hide_delay
|
integer
|
600
|
|
style
|
string
|
nil
|
|
type
|
string
|
"hover"
|
dir
string
Default
"ltr"
id
string
Default
—
scroll_hide_delay
integer
Default
600
style
string
Default
nil
type
string
Default
"hover"
Viewport
The viewport area of the scroll area.
| Prop | Type | Default | Description |
|---|
Scrollbar
The vertical scrollbar. Add a second scrollbar with an orientation attribute to enable horizontal scrolling.
| Prop | Type | Default | Description |
|---|---|---|---|
force_mount
|
boolean
|
false
|
|
orientation
|
string
|
"vertical"
|
force_mount
boolean
Default
false
orientation
string
Default
"vertical"
Data attributes
| Attribute | Values | Description |
|---|---|---|
[data-state]
|
visible | hidden
|
Reflects whether the scrollbar is visible. |
[data-orientation]
|
vertical | horizontal
|
The orientation of the scrollbar. |
Thumb
The thumb to be used in ScrollArea.scrollbar.
| Prop | Type | Default | Description |
|---|
Data attributes
| Attribute | Values | Description |
|---|---|---|
[data-state]
|
visible | hidden
|
Reflects whether the thumb is visible. |
Corner
The corner where both vertical and horizontal scrollbars meet.
| Prop | Type | Default | Description |
|---|
Accessibility
In most cases, it's best to rely on native scrolling and work with the customization options available in CSS. When that isn't enough, ScrollArea provides additional customizability while maintaining the browser's native scroll behavior (as well as accessibility features, like keyboard scrolling).
Keyboard Interactions
Scrolling via keyboard is supported by default because the component relies on native scrolling. Specific keyboard interactions may differ between platforms, so we do not specify them here or add specific event listeners to handle scrolling via key events.