Skip to main content

useDraggable

Make elements draggable

Usage

Fixed Demo

Live Editor
function Demo() {
  const el = useRef<HTMLDivElement>(null);

  const [initialValue, setInitialValue] = useState({ x: 200 / 2.2, y: 120 });

  useEffect(() => {
    setInitialValue({ x: window.innerWidth / 2.2, y: 120 });
  }, []);

  const [x, y, isDragging] = useDraggable(el, {
    initialValue,
    preventDefault: true,
  });

  return (
    <div>
      <p style={{ textAlign: "center" }}>Check the floating boxes</p>
      <div
        ref={el}
        style={{
          position: "fixed",
          cursor: "move",
          zIndex: 10,
          touchAction: "none",
          padding: 10,
          border: "solid 1px",
          left: x,
          top: y,
        }}
      >
        {isDragging ? "Dragging!" : "👋 Drag me!"}
        <div>
          I am at {Math.round(x)}, {Math.round(y)}
        </div>
      </div>
    </div>
  );
}
Result
Loading...

Relative Demo

Live Editor
function Demo() {
  const el = useRef<HTMLDivElement>(null);
  const scope = useRef<HTMLDivElement>(null);

  const initialValue = { x: 200 / 2.2, y: 120 };

  const [x, y, isDragging, setPosition] = useDraggable(el, {
    initialValue,
    preventDefault: true,
    containerElement: scope,
  });

  return (
    <div
      ref={scope}
      style={{
        width: 500,
        height: 500,
        border: "1px solid blue",
        position: "relative",
      }}
    >
      <button
        style={{ textAlign: "center" }}
        onClick={() => {
          setPosition({
            x: 250,
            y: 250,
          });
        }}
      >
        Set Position
      </button>
      <div
        ref={el}
        style={{
          position: "absolute",
          cursor: "move",
          zIndex: 10,
          touchAction: "none",
          padding: 10,
          border: "solid 1px",
          left: x,
          top: y,
          whiteSpace: "nowrap",
        }}
      >
        {isDragging ? "Dragging!" : "👋 Drag me!"}
        <div style={{ whiteSpace: "nowrap" }}>
          I am at {Math.round(x)}, {Math.round(y)}
        </div>
      </div>
    </div>
  );
}
Result
Loading...

API

useDraggable

Returns

readonly [number, number, boolean, React.Dispatch<React.SetStateAction<Position>>]: A tuple with the following elements:

  • x
  • y
  • Whether the element is being dragged set the element position

Arguments

ArgumentDescriptionTypeDefaultValue
targetdom elementBasicTarget<HTMLElement | SVGElement> (Required)-
optionsoptional paramsUseDraggableOptions | undefined-

UseDraggableOptions

PropertyDescriptionTypeDefaultValue
exactOnly start the dragging when click on the element directlybooleanfalse
preventDefaultPrevent events defaultsbooleanfalse
stopPropagationPrevent events propagationbooleanfalse
draggingElementElement to attach pointermove and pointerup events to.BasicTarget<HTMLElement | SVGElement>window
containerElementElement for calculating bounds (If not set, it will use the event's target).BasicTarget<HTMLElement | SVGAElement>undefined
handleHandle that triggers the drag eventRefObject<HTMLElement | SVGElement>target
pointerTypesPointer types that listen to.PointerType[]['mouse', 'touch', 'pen']
initialValueInitial position of the element.Position{ x: 0, y: 0 }
onStartCallback when the dragging starts. Return false to prevent dragging.(position: Position, event: PointerEvent) => void | false-
onMoveCallback during dragging.(position: Position, event: PointerEvent) => void-
onEndCallback when dragging end.(position: Position, event: PointerEvent) => void-

BasicTarget

export type BasicTarget<T extends TargetType = Element> = (() => TargetValue<T>) | TargetValue<T> | MutableRefObject<TargetValue<T>>;

TargetValue

type TargetValue<T> = T | undefined | null;

TargetType

type TargetType = HTMLElement | Element | Window | Document | EventTarget;

PointerType

export type PointerType = 'mouse' | 'touch' | 'pen';

Position

export interface Position {
x: number;
y: number;
}