All hooks / Events and input
useDrag
Calls onDrag while the user drags an element with a mouse, finger, or pen. Returns a callback ref for the element (the handle).
Captures the pointer, so the drag keeps going when the pointer leaves the element or the window, and ends on release or cancel. Movements under threshold don't start a drag, so a click on the handle stays a click. Text selection is blocked while dragging. Always calls the latest onDrag.
Import
import { useDrag } from '@vincent.ri/react-tils'
// or only this hook
import { useDrag } from '@vincent.ri/react-tils/useDrag'Usage
// A splitter that resizes the sidebar.
const [width, setWidth] = useState(280)
const handle = useDrag(({ deltaX }) => setWidth((w) => Math.min(600, Math.max(160, w + deltaX))))
return (
<div style={{ display: 'flex' }}>
<aside style={{ width }}>…</aside>
<div ref={handle} role="separator" style={{ width: 6, cursor: 'col-resize', touchAction: 'none' }} />
<main>…</main>
</div>
)Live demo
Drag the bar between the panes, even past the edge.
When to use it
Resizable panels and splitters, custom sliders, dragging items on a canvas or board, or pan to scroll.
When not to
You only need to know a swipe happened and its direction; use useSwipe. For reordering lists, a drag-and-drop library handles keyboard access and drop targets. Give touch handles CSS touch-action: none, or the browser scrolls instead of dragging.
API
function useDrag<T extends HTMLElement = HTMLElement>(onDrag: (drag: DragState) => void, options?: DragOptions): (element: T | null) => void| Parameter | Description |
|---|---|
onDrag | Called on every move with dx, dy, deltaX, deltaY, first, last, and the event. Can change every render. |
options | enabled, threshold. Optional. |
Returns A callback ref. Pass it to the element to drag.
Types
export type DragState = {
/** Distance moved since the drag started, in pixels. */
dx: number;
dy: number;
/** Distance moved since the previous call. */
deltaX: number;
deltaY: number;
/** The first call of this drag. */
first: boolean;
/** The last call: the pointer was released or the drag was cancelled. */
last: boolean;
event: PointerEvent;
};
export type DragOptions = {
/** Listen while `true`. Default `true`. */
enabled?: boolean;
/** Pixels the pointer must move before a drag starts, so clicks stay clicks. Default `3`. */
threshold?: number;
};Browser support
| Uses | Chrome | Edge | Firefox | Safari | Chrome Android | Safari iOS |
|---|---|---|---|---|---|---|
| Pointer eventsWidely available | 55+ | 12+ | 59+ | 13+ | 55+ | 13+ |
| setPointerCaptureWidely available | 55+ | 12+ | 59+ | 13+ | 55+ | 13+ |
First version with support, from MDN's compatibility data (web-features 3.42.0, @mdn/browser-compat-data 8.1.5).
Coming from another library?
Other libraries call this useDraggable, useGesture (onDrag), usePointerDrag.