Introducing PrimeReact v11-alpha 🎉Discover Now

useScrollArea

Hook that manages custom scrollbar state, overflow detection, and handle positioning.

basic-demo

Usage#

import { useScrollArea } from '@primereact/headless/scrollarea';
const { rootProps, viewportProps, contentProps, scrollbarYProps, handleYProps, hiddenState } = useScrollArea();
 
<div {...rootProps}>
    <div {...viewportProps} style={{ overflow: 'scroll', scrollbarWidth: 'none' }}>
        <div {...contentProps}></div>
    </div>
    {!hiddenState.y && (
        <div {...scrollbarYProps}>
            <div {...handleYProps} />
        </div>

useScrollArea returns pre-wired prop objects for every part of a custom scrollbar — root, viewport, content, scrollbars, handles, and corner — along with state flags that tell you which pieces are actually needed. See Primitive for a component-based API.

Features#

  • Part-level prop spreading — spread-ready objects for root, viewport, content, scrollbars, handles, and corner with built-in ref callbacks
  • Automatic handle sizing — handle dimensions recompute from the viewport-to-content ratio via ResizeObserver
  • Pointer drag handling — dragging both the handle and the track to scroll is handled internally
  • Overflow edge detection — granular flags for when content is scrolled past the top, bottom, left, or right edge
  • Visibility hints — hiddenState tells you which scrollbars and the corner can be skipped entirely
  • Visibility variants — variant controls whether scrollbars are always on, hover-only, scroll-only, auto, or hidden

Working with callbacks#

Pick a visibility behavior#

Choose variant based on how visible you want scrollbars to be at rest.

const scrollArea = useScrollArea({ variant: 'hover' });

Available values: 'auto' (default), 'hover', 'scroll', 'always', 'hidden'.

Skip unnecessary scrollbars#

Use hiddenState to avoid rendering scrollbar elements when content does not overflow on that axis, which keeps the markup minimal and prevents phantom interaction areas.

const { hiddenState } = useScrollArea();
 
// hiddenState.x      → true when horizontal scrollbar is unnecessary
// hiddenState.y      → true when vertical scrollbar is unnecessary
// hiddenState.corner → true when the corner element is unnecessary

Render scroll-edge indicators#

Read state.scrollYBefore, state.scrollYAfter, state.scrollXBefore, and state.scrollXAfter to render fade masks, shadow indicators, or trigger infinite loading at the edges.

const { state } = useScrollArea();
 
{
    state.scrollYBefore && <div className="top-fade" />;
}
{
    state.scrollYAfter && <div className="bottom-fade" />;
}

Both-axis scrollbars with a corner#

When content overflows in both directions, render both scrollbars and the corner using the orientation-specific prop objects.

const { scrollbarYProps, scrollbarXProps, handleYProps, handleXProps, cornerProps, hiddenState } = useScrollArea();
 
{
    !hiddenState.y && (
        <div {...scrollbarYProps}>
            <div {...handleYProps} />
        </div>
    );
}
{
    !hiddenState.x && (
        <div {...scrollbarXProps}>
            <div {...handleXProps} />
        </div>
    );
}
{
    !hiddenState.corner && <div {...cornerProps} />;
}

Position the handle via CSS variables#

The hook writes handle dimensions to CSS custom properties; read them on the handle element instead of managing size imperatively.

VariableDescription
--handle-heightCalculated handle height (vertical bar)
--handle-widthCalculated handle width (horizontal bar)
--handle-offsetCalculated handle offset along the track
<div style={{ height: 'var(--px-handle-height)', transform: 'translate3d(0, var(--px-handle-offset, 0), 0)' }} />

Styling with data attributes#

Every prop object includes data-scope="scrollarea" and a data-part attribute for targeted CSS selectors.

[data-scope='scrollarea'][data-part='scrollbar-y'] {
    width: 8px;
}
 
[data-scope='scrollarea'][data-part='handle-y'] {
    background: rgba(0, 0, 0, 0.3);
    border-radius: 4px;
}

API#

useScrollArea#

NameTypeDefault
stepnumber5
Step factor to scroll the content while pressing the arrow keys.
variant"hidden" | "auto" | "always" | "scroll" | "hover"'auto'
Controls scrollbar visibility behavior.
maskbooleanfalse
When enabled, a fade mask is applied to the viewport edges to indicate overflow.
tabIndexnumber0
Tab index for the viewport element to make it keyboard focusable.

Accessibility#

Arrow keys and PageUp/PageDown scroll when focus is inside, matching native scroll behavior. See Primitive for full WAI-ARIA compliance details.