Skip to main content
Version: Next

useDrag

The useDrag hook allows to add drag gesture recognition to any DOM element in a React component. It handles pointer events under the hood, calculates movement, offset, velocity, and exposes a powerful callback-based API for interacting with drag state in real-time.

Syntax

useDrag(
refs: RefObject<HTMLElement> | RefObject<HTMLElement>[],
callback: (event: DragEvent) => void,
config?: DragConfig
): void

Parameters

NameTypeDescription
Refs (required)RefObject<HTMLElement> or RefObject<HTMLElement>[]Single or array of refs to DOM elements to make draggable.
Callback (required)(event:DragEvent) => voidCalled during drag and when drag ends
Config (required)DragConfigOptional config to customize drag behaviour

Callback Signature

type DragEvent = {
index: number;
down: boolean; // True when dragging
movement: { x: number; y: number }; // Delta from start of drag
offset: { x: number; y: number }; // Total offset (absolute)
velocity: { x: number; y: number }; // Velocity in px/ms
event: PointerEvent; // Native pointer event
cancel: () => void; // Cancel pointer capture
};

DragConfig Options

type DragConfig = {
threshold?: number; // Min distance before triggering drag
axis?: 'x' | 'y'; // Lock movement to single axis
initial?: () => { x: number; y: number }; // Custom initial offset
};

Examples

1. Basic Dragging of a Single Box

This example demonstrates dragging a single element along both axes.
The element resets to its original position when the drag ends.

import React, { useRef } from 'react';
import { animate, useDrag, useValue, withSpring } from 'react-ui-animate';

const App = () => {
  const ref = useRef<HTMLDivElement>(null);
  const [translateX, setTranslateX] = useValue(0);
  const [translateY, setTranslateY] = useValue(0);

  useDrag(ref, ({ down, movement }) => {
    setTranslateX(down ? movement.x : withSpring(0));
    setTranslateY(down ? movement.y : withSpring(0));
  });

  return (
    <animate.div
      ref={ref}
      style={{
        cursor: 'grab',
        translateX,
        translateY,
        width: 100,
        height: 100,
        backgroundColor: 'teal',
        borderRadius: 4,
      }}
    />
  );
};

export default App;

2. Dragging Multiple Boxes Independently

Here, multiple boxes are draggable independently on the X-axis.
Each box remembers its drag position while dragging, and springs back to its original position when released.

import React, { createRef, useMemo, useRef } from 'react';
import { animate, useValue, useDrag, withSpring } from 'react-ui-animate';

const App = () => {
  const items = useRef(Array.from({ length: 5 }, () => 0));
  const [positions, setPositions] = useValue(items.current);

  const refs = useMemo(
    () => Array.from({ length: 3 }, () => createRef<HTMLDivElement>()),
    []
  );

  useDrag(refs, function ({ down, movement, index }) {
    if (down) {
      const newPositions = [...items.current];
      newPositions[index] = movement.x;
      setPositions(newPositions);
    } else {
      setPositions(withSpring(items.current));
    }
  });

  return (
    <>
      {refs.map((r, i) => (
        <animate.div
          key={i}
          ref={r}
          style={{
            width: 100,
            height: 100,
            cursor: 'grab',
            backgroundColor: 'teal',
            borderRadius: 4,
            marginBottom: 10,
            translateX: positions[i],
          }}
        />
      ))}
    </>
  );
};

export default App;