Skip to contents

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.

Renew passport
Default
Water the plants
Reply to Alex
Book a dentist
Send the invoice
Plan the sprint
Call the bank
Buy groceries
Clean the desk
Book the flights
Review the draft
Update the budget
Move the standup
Copy the keys
Sort the photos
Reboot the router
Wrap the gift
maxSpeed={150}
Pay the rent
Update resume
Back up the laptop
Read the docs
Fix the bug
Write the tests
Email the team
Order supplies
Write the changelog
Bump the deps
Fix the flaky test
Tag the release
Check the metrics
Onboard the intern
Book the retro
Archive the branch

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:

Anatomy

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.

A code block that stays put

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.

Off while the list is filtered

Use canScroll when the decision depends on the drag source. It runs on every frame.

Scrolling only for unpinned cards

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.

Scrolling for cards, not for anything else

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.

Wake up
Coffee
Standup
Code review
Lunch
Design sync
Focus block
Errands
Gym
Dinner
Reading
Sleep
Horizontal only

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:

An axis that follows the drag

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.

Slower in a short list

You can also pass a callback that runs on every scrolling frame. For example, derive the speed from the remaining scroll distance.

Faster the more there is to cross

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.

Archive
Kickoff
Research

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.

A canvas that pans itself

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.

A canvas with bounds

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:

One element, 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>.

A page that never scrolls sideways

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.

A page that only ever scrolls down

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
Description

Whether this provider’s inferred auto-scroll activation is disabled.

Type
Default
false
childrenReact.ReactNode
Description

The application subtree rendered by this provider.

Type

DragAutoScroll.Provider.PropsHide

Re-Export of Provider props as DragAutoScrollProviderProps

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'
Description

Which axis to scroll on. Accepts a static value or a callback evaluated every frame.

Type
Default
'all'
applyScrollUnion
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
Description

Return false to disable scrolling on this element for the current drag. Evaluated every frame, so scrolling can be suspended dynamically.

Type
maxSpeedUnion900
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 of 0 stops this container and lets an ancestor scroll, which is equivalent to returning false from canScroll.

Type
Default
900
disabledbooleanfalse
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 canScroll instead.

Type
Default
false
classNamestring | function
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 ReactElement or 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.

DragAutoScroll.Root.PropsHide

Re-Export of Root props as DragAutoScrollRootProps

DragAutoScroll.Root.StateHide