Scroll Area

Augments native scroll functionality for custom, cross-browser styling.

Features

  • Track sits on top of the scrollable content, taking up no space.
  • Scrolling is native; no underlying position movements via CSS transformations.
  • Shims pointer behaviors only when interacting with the controls, so keyboard controls are unaffected.
  • Progressively enhanced so that content is avalaible on devices where custom controls aren't supported.

Install the component from your command line.

npm install @radix-ui/react-scroll-area

Import the components and piece the parts together.

import * as ScrollArea from '@radix-ui/react-scroll-area';
export default () => (
<ScrollArea.Root>
<ScrollArea.Viewport />
<ScrollArea.ScrollbarX>
<ScrollArea.Track>
<ScrollArea.ButtonStart />
<ScrollArea.Thumb />
<ScrollArea.ButtonEnd />
</ScrollArea.Track>
</ScrollArea.ScrollbarX>
<ScrollArea.ScrollbarY>
<ScrollArea.Track>
<ScrollArea.ButtonStart />
<ScrollArea.Thumb />
<ScrollArea.ButtonEnd />
</ScrollArea.Track>
</ScrollArea.ScrollbarY>
<ScrollArea.Corner />
</ScrollArea.Root>
);

Create your styled scroll area component from the primitive parts.

import { styled } from 'path-to/stitches.config';
import * as ScrollArea from '@radix-ui/react-scroll-area';
const { SCROLL_AREA_CSS_PROPS } = ScrollArea;
const StyledScrollArea = styled(ScrollArea.Root, {
position: 'relative',
zIndex: 0,
maxWidth: '100%',
maxHeight: '100%',
'& [data-radix-scroll-area-viewport-position]::-webkit-scrollbar': {
display: 'none',
},
});
const StyledViewport = styled(ScrollArea.Viewport, {
zIndex: 1,
position: 'relative',
});
const StyledScrollbarY = styled(ScrollArea.ScrollbarY, {
zIndex: 2,
position: 'absolute',
userSelect: 'none',
transition: '300ms opacity ease',
width: 8,
right: 0,
top: 0,
bottom: 0,
});
const StyledTrack = styled(ScrollArea.Track, {
zIndex: -1,
position: 'relative',
width: '100%',
height: '100%',
});
const StyledThumb = styled(ScrollArea.Thumb, {
backgroundColor: 'gainsboro',
position: 'absolute',
top: 0,
left: 0,
userSelect: 'none',
borderRadius: 9999,
willChange: `var(${SCROLL_AREA_CSS_PROPS.scrollbarThumbWillChange})`,
height: `var(${SCROLL_AREA_CSS_PROPS.scrollbarThumbHeight})`,
width: `var(${SCROLL_AREA_CSS_PROPS.scrollbarThumbWidth})`,
});
export default () => (
<div style={{ height: 250 }}>
<StyledScrollArea>
<StyledViewport>
<div style={{ height: 1000, backgroundImage: 'repeating-linear-gradient(0deg, dodgerblue, dodgerblue 10px, transparent 10px, transparent 20px)', }} />
</StyledViewport>
<StyledScrollbarY>
<StyledTrack>
<StyledThumb />
</StyledTrack>
</StyledScrollbarY>
</StyledScrollArea>
</div>
);

Contains all the parts of a scroll area.

PropTypeDefault
asenumdiv
overflowXenum"auto"
overflowYenum"auto"
scrollbarVisibilityenum"hover"
scrollbarVisibilityRestTimeoutnumber600
trackClickBehavioremum"relative"

The viewport area of the scroll area.

PropTypeDefault
asenumdiv

The horizontal scrollbar.

PropTypeDefault
asenumdiv

The vertical scrollbar.

PropTypeDefault
asenumdiv

The start button to be used in ScrollArea.ScrollbarX and ScrollArea.ScrollbarY.

PropTypeDefault
asenumdiv

The end button to be used in ScrollArea.ScrollbarX and ScrollArea.ScrollbarY.

PropTypeDefault
asenumdiv

The track to be used in ScrollArea.ScrollbarX and ScrollArea.ScrollbarY.

PropTypeDefault
asenumdiv

The thumb to be used in ScrollArea.ScrollbarX and ScrollArea.ScrollbarY.

PropTypeDefault
asenumdiv

The corner where both vertical and horizontal scrollbars meet.

PropTypeDefault
asenumdiv

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.