Drag Auto Scroll
A component that enables and configures drag auto-scroll.
Drag the card into either list, at the slot you want. The provider enables both; only the second list configures its region.
Auto-scroll is opt-in. The board above uses one DragAutoScroll.Provider to enable automatic scrolling for both lists. The second list also uses DragAutoScroll.Root to reduce its speed.
Once a provider is mounted, Base UI detects nested scroll containers around drag sources, the pointer, and drop targets. Scrollable elements need no additional props. Use DragAutoScroll.Root to configure an existing scroll container or implement custom scrolling, such as panning a canvas with a CSS transform.
It pairs with Draggable and DropTarget. For auto-scroll inside a reorderable list, see the collections guide. Auto-scroll runs for pointer drags only; a keyboard drag scrolls the focused target into view instead.
Anatomy
Import the component and wrap the application area whose lifetime should enable auto-scroll:
The provider renders no element and does not scope drag and drop through React context. Base UI uses one drag manager per page, so mounting any provider enables automatic scrolling for every Base UI drag source. Set disabled to disable this provider without removing it from the React tree. Another mounted provider or root can still keep auto-scroll active.
Which containers scroll
While a DragAutoScroll.Provider, DragAutoScroll.Root, or imperative auto-scroller registration is mounted, scrollable ancestors under the pointer and around the dragged element can auto-scroll, including the page.
An element participates on axes whose computed overflow is auto, scroll, or overlay. Use hidden or clip to exclude an axis.
A container scrolls while the pointer is in an edge zone and more content remains in that direction. Each edge zone is one quarter of the container’s size on that axis, capped at 180px.
Use canScroll to exclude a scrollable container. Use applyScroll to implement scrolling without element scroll offsets.
Drop targets are resolved again as content scrolls, so a target that moves under a stationary pointer can receive the drop.
Customize a container that already scrolls
DragAutoScroll.Root can enable auto-scroll for one region without a provider. Inside a provider, use it to configure the accepted drags, allowed axes, speed, or disabled state for one container.
Control when it scrolls
Use disabled to exclude a container from auto-scroll. An ancestor can then scroll on the excluded axes. This is useful for code blocks, embedded maps, and small scroll containers that should not move while the pointer crosses them.
You can also pass an expression. The registration remains active, so changing the value during a drag pauses or resumes scrolling without re-registering the container.
Use canScroll when the decision depends on the drag source. It runs on every frame.
Use accept to limit the element to one or more drag kinds. Other drags do not use the container. The accepted kinds determine the payload type passed to per-frame callbacks.
Constrain the direction
Use allowedAxis to limit scrolling to one axis.
CSS usually determines the scrollable axes. A lane with overflow-x: auto scrolls horizontally and never vertically. Use allowedAxis for an applyScroll implementation without scrollable CSS, or for a container that can scroll on both axes but should only use one. The lane below sets allowedAxis="horizontal" explicitly.
Drag a stop toward the left or right edge and the lane scrolls to follow. It only scrolls sideways, so moving the pointer up or down never scrolls it.
You can also pass a callback that runs every frame. For example, a grid can scroll vertically for row drags and horizontally for column drags:
Tune the speed
Use maxSpeed to set the speed at the container edge in CSS pixels per second. The default is 900. Increase it for a large scroll range or reduce it for a short list.
maxSpeed is reached at the container edge. Speed increases with the pointer’s depth in the edge zone and ramps up over the first 400ms of continuous scrolling.
You can also pass a callback that runs on every scrolling frame. For example, derive the speed from the remaining scroll distance.
Set it to 0 to stop the container and let an ancestor scroll on those axes. Returning false from canScroll has the same effect.
Implement custom scrolling
Use applyScroll when Base UI cannot scroll an element directly. For example, a canvas panned with a CSS transform has no scroll offsets to update. The callback receives the scroll delta for the current frame, which you can apply to the canvas camera.
The element does not need scrollable overflow, and Base UI does not read its scroll extent. Edge zones, speed ramping, and nesting work as they do for a scroll container.
Drag a pin to the bottom edge and hold still. The canvas has nothing to scroll, so it moves its own camera, and the archive scrolls into reach.
Archived: nothing yet
Register the element that clips the canvas, not the transformed content inside it. The content’s bounding rect moves with the camera, which would also move the edge zones.
The x and y values match the arguments Base UI would pass to element.scrollBy(). A positive x moves the view right, so the content moves left. Both values use CSS pixels and include the speed ramp and elapsed frame time.
Apply the movement synchronously before returning. Base UI resolves the drop target again on the next frame. Updating the camera through React state would make hit testing one frame late, while writing the transform from a ref keeps it current.
Return the axes that moved, either 'horizontal', 'vertical', or 'all'. An ancestor can then scroll on any remaining axis. Returning nothing claims every active axis, which suits an unbounded canvas. Return false, 'none', or null when neither axis moved. A bounded canvas should release an axis at its limit so an ancestor can scroll instead.
Use allowedAxis and accept to define how a custom implementation responds. Base UI cannot infer this behavior from CSS, and a canvas may receive drags that should not pan it.
Nested containers
The innermost container scrolls first and consumes the axes it moves on; an ancestor takes over only on the axes left unconsumed. A card dragged to the bottom of an inner list scrolls that list, and reaches the page only once the list hits its end. A column that scrolls vertically inside a board that scrolls horizontally therefore splits the two axes between them.
Nesting follows the DOM tree and requires no separate configuration. Detected scroll containers and DragAutoScroll.Root elements use the same ancestor order.
Both a scroll container and a drop target
You can pass a DragAutoScroll.Root to a DropTarget.Root’s render prop when one element needs both roles:
Scrolling the page
The inferred walk ends at the document root, so dragging near the viewport edge can scroll the page after auto-scroll has been enabled by a mounted provider, root, or imperative registration. Its edge zones follow the viewport, and inner containers still win.
You can stop page auto-scroll on an axis with overflow: hidden or clip on <html> or <body>.
That is also why a scroll lock holds during a drag: modal Dialog and Popover configurations that apply overflow: hidden prevent the page behind them from auto-scrolling.
Use useDragDropManager to customize page auto-scroll or a scroll container rendered by code you do not control.
You can add disabled: true to the same call to switch the page off entirely.
API reference
The accept value types the drag the per-frame callbacks see: accept={card}
hands canScroll and allowedAxis a source carrying the card’s payload. The
generated table below renders those signatures at the default type (unknown),
because the reference is extracted without concrete type arguments.
Provider
disabledbooleanfalse
- Name
- Description
Whether this provider’s inferred auto-scroll activation is disabled.
- Type
- Default
false
childrenReact.ReactNode—
- Name
- Description
The application subtree rendered by this provider.
- Type
Root
acceptUnion—
- Name
- Description
One or more drag source kinds that can scroll this element. Omit it to scroll for every drag.
An unaccepted drag does not scroll this element, even when it is a detected scroll container. The accepted kinds determine the payload type passed to per-frame callbacks.
- Type
allowedAxisUnion'all'
- Name
- Description
Which axis to scroll on. Accepts a static value or a callback evaluated every frame.
- Type
- Default
'all'
applyScrollUnion—
- Name
- Description
Applies the frame’s scroll delta with custom logic. Use it for a canvas moved by a CSS
transform. The element does not need scrollable overflow, and Base UI does not read its scroll extent.Apply the movement synchronously before returning. Base UI resolves the drop target under the pointer again on the next frame.
- Type
canScrollfunction—
- Name
- Description
Return
falseto disable scrolling on this element for the current drag. Evaluated every frame, so scrolling can be suspended dynamically.- Type
maxSpeedUnion900
- Name
- Description
How fast the container moves at the deepest point of an edge zone, in CSS pixels per second. Accepts a static value or a callback evaluated every frame the container is engaged.
The default is
900. Increase it for a large scroll range or reduce it for a short list. A value of0stops this container and lets an ancestor scroll, which is equivalent to returningfalsefromcanScroll.- Type
- Default
900
disabledbooleanfalse
- Name
- Description
Whether to disable auto-scroll for this element, including when Base UI detects it as a scroll container. An ancestor can scroll on the excluded axes.
Base UI reads this value every frame and keeps the registration active. Changing it during a drag pauses or resumes scrolling without re-registering the element.
For a decision that depends on the drag, use
canScrollinstead.- Type
- Default
false
classNamestring | function—
- Name
- Description
CSS class applied to the element, or a function that returns a class based on the component’s state.
- Type
styleReact.CSSProperties | function—
- Name
- Description
Style applied to the element, or a function that returns a style object based on the component’s state.
- Type
renderReactElement | function—
- Name
- Description
Allows you to replace the component’s HTML element with a different tag, or compose it with another component.
Accepts a
ReactElementor a function that returns the element to render.- Type
data-disabled
Present while auto-scrolling is disabled.
Attribute | Description | |
|---|---|---|
data-disabled | Present while auto-scrolling is disabled. | |