Augments native scroll functionality for custom, cross-browser styling.
import React from 'react';
import { styled } from '@stitches/react';
import { violet, mauve, blackA } from '@radix-ui/colors';
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
const SCROLLBAR_SIZE = 10;
const StyledScrollArea = styled(ScrollAreaPrimitive.Root, {
width: 200,
height: 225,
borderRadius: 4,
overflow: 'hidden',
boxShadow: `0 2px 10px ${blackA.blackA7}`,
});
const StyledViewport = styled(ScrollAreaPrimitive.Viewport, {
width: '100%',
height: '100%',
borderRadius: 'inherit',
});
const StyledScrollbar = styled(ScrollAreaPrimitive.Scrollbar, {
display: 'flex',
// ensures no selection
userSelect: 'none',
// disable browser handling of all panning and zooming gestures on touch devices
touchAction: 'none',
padding: 2,
background: blackA.blackA6,
transition: 'background 160ms ease-out',
'&:hover': { background: blackA.blackA8 },
'&[data-orientation="vertical"]': { width: SCROLLBAR_SIZE },
'&[data-orientation="horizontal"]': {
flexDirection: 'column',
height: SCROLLBAR_SIZE,
},
});
const StyledThumb = styled(ScrollAreaPrimitive.Thumb, {
flex: 1,
background: mauve.mauve10,
borderRadius: SCROLLBAR_SIZE,
// increase target size for touch devices https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
position: 'relative',
'&::before': {
content: '""',
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: '100%',
height: '100%',
minWidth: 44,
minHeight: 44,
},
});
const StyledCorner = styled(ScrollAreaPrimitive.Corner, {
background: blackA.blackA8,
});
// Exports
export const ScrollArea = StyledScrollArea;
export const ScrollAreaViewport = StyledViewport;
export const ScrollAreaScrollbar = StyledScrollbar;
export const ScrollAreaThumb = StyledThumb;
export const ScrollAreaCorner = StyledCorner;
// Your app...
const Box = styled('div', {});
const Text = styled('div', {
color: violet.violet11,
fontSize: 15,
lineHeight: '18px',
fontWeight: 500,
});
const Tag = styled('div', {
color: mauve.mauve12,
fontSize: 13,
lineHeight: '18px',
marginTop: 10,
borderTop: `1px solid ${mauve.mauve6}`,
paddingTop: 10,
});
const TAGS = Array.from({ length: 50 }).map((_, i, a) => `v1.2.0-beta.${a.length - i}`);
const ScrollAreaDemo = () => (
<ScrollArea>
<ScrollAreaViewport css={{ backgroundColor: 'white' }}>
<Box style={{ padding: '15px 20px' }}>
<Text>Tags</Text>
{TAGS.map((tag) => (
<Tag key={tag}>{tag}</Tag>
))}
</Box>
</ScrollAreaViewport>
<ScrollAreaScrollbar orientation="vertical">
<ScrollAreaThumb />
</ScrollAreaScrollbar>
<ScrollAreaScrollbar orientation="horizontal">
<ScrollAreaThumb />
</ScrollAreaScrollbar>
<ScrollAreaCorner />
</ScrollArea>
);
export default ScrollAreaDemo;
Install the component from your command line.
npm install @radix-ui/react-scroll-area
Import all parts and piece them together.
import * as ScrollArea from '@radix-ui/react-scroll-area';
export default () => (
<ScrollArea.Root>
<ScrollArea.Viewport />
<ScrollArea.Scrollbar orientation="horizontal">
<ScrollArea.Thumb />
</ScrollArea.Scrollbar>
<ScrollArea.Scrollbar orientation="vertical">
<ScrollArea.Thumb />
</ScrollArea.Scrollbar>
<ScrollArea.Corner />
</ScrollArea.Root>
);
Contains all the parts of a scroll area.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
type | enum | "hover" |
scrollHideDelay | number | 600 |
dir | enum |
The viewport area of the scroll area.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
The vertical scrollbar. Add a second Scrollbar
with an orientation
prop to enable horizontal scrolling.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
forceMount | boolean | |
orientation | enum | vertical |
The thumb to be used in ScrollArea.Scrollbar
.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
The corner where both vertical and horizontal scrollbars meet.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
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 accessibiliy features, like keyboard scrolling).
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.