useDraggable
A React hook that creates a Draggable instance for orchestrating sensors and moving DOM elements.
Usage
import { useRef, useMemo, useCallback } from 'react';
import { usePointerSensor, useKeyboardSensor, useDraggable } from 'dragdoll-react';
function DraggableBox() {
const elementRef = useRef<HTMLDivElement | null>(null);
const [pointerSensor, setPointerSensorElementRef] = usePointerSensor();
const [keyboardSensor, setKeyboardSensorElementRef] = useKeyboardSensor();
const draggableSettings = useMemo(
() => ({
startPredicate: (e) => {
const areStarsAligned = Math.random() > 0.5;
return areStarsAligned;
},
// NB: If you don't define the elements setting, no elements will be
// moved during the drag. This is the single most important setting to
// define! It's optional, because technically there are a few use cases
// where you might not want to move any elements during the drag, but
// trigger e.g. auto-scrolling only.
elements: () => (elementRef.current ? [elementRef.current] : []),
}),
[],
);
const draggable = useDraggable([pointerSensor, keyboardSensor], draggableSettings);
const setRefs = useCallback(
(node: HTMLDivElement | null) => {
elementRef.current = node;
setPointerSensorElementRef(node);
setKeyboardSensorElementRef(node);
},
[setPointerSensorElementRef, setKeyboardSensorElementRef],
);
return (
<div
ref={setRefs}
style={{ position: 'relative', width: '100px', height: '100px', backgroundColor: 'red' }}
>
Drag me
</div>
);
}Signature
function useDraggable<S extends Sensor = Sensor>(
sensors: (S | null)[],
settings?: UseDraggableSettings<S>,
): Draggable<S> | null;Parameters
sensors
Array of sensor instances. Sensors can be null while initializing. The hook will instantiate the draggable when sensors are available. When sensors change, they are updated in-place without recreating the draggable instance.
settings
Configuration settings for the Draggable instance. Extends core DraggableOptions (excluding container) with additional React-specific settings.
WARNING
You MUST memoize the settings object (e.g. with useMemo) to prevent unnecessary re-evaluations and performance issues.
As per React's declarative nature, these settings are always merged with the default settings and then provided to the Draggable instance. This way there will be no cumulative effect of settings changes over time meaning that the old settings will be completely overridden by the new settings.
Treat these as live settings that can be updated dynamically without recreating the draggable (except for id, which will cause the draggable to be recreated). When dndGroups or computeClientRect change, the hook will automatically trigger collision detection updates in the associated DndObserver.
dragPreview
type dragPreview = boolean | undefined;If true, generates a high-performance proxy element in document.body that receives all drag movements instead of your original elements. Your original elements stay perfectly in place.
You should use the <DragPreview> component to render dynamic visuals directly into this proxy element. By using proxies, you completely bypass React's virtual DOM reparenting limits while preserving 60-120fps hardware-accelerated transforms.
- Optional.
- Default is
undefined.
dragPreviewContainer
type dragPreviewContainer = HTMLElement | (() => HTMLElement);The container element to reparent drag preview proxy elements into during drag. Only used when dragPreview is true.
This is useful when you need the proxy to live in a specific stacking context, shadow DOM, or iframe.
- Optional.
- Default is
document.body.
dragPreviewExitTimeout
type dragPreviewExitTimeout = number | undefined;If set to a positive number, enables exit animation for drag previews. When the drag ends, the proxy element stays alive in an "exiting" state instead of being removed immediately. The <DragPreview> render function receives exiting: true and a done() callback. Call done() when your animation finishes to remove the proxy.
If done() is not called within this many milliseconds, the proxy is removed automatically as a safety fallback.
Only used when dragPreview is true.
- Optional.
- Default is
undefined(no exit animation, proxy removed immediately on drag end).
id
type id = DraggableId;The id is a unique identifier for the draggable that is assigned as the draggable's id property.
IMPORTANT
The Draggable instance will be automatically recreated when the id setting is explicitly provided and changed.
- Optional.
- Default is a unique symbol.
dndObserver
type dndObserver = DndObserver<any> | null;If this setting is not provided, the draggable will be registered to the DndObserver instance from the DndObserverContext automatically (if available).
However, here you can explicitly provide a DndObserver instance to register the draggable with. This setting will take precedence over the context observer.
Set to null to explicitly opt out of the automatic context observer registration, if you e.g. don't want this draggable to be registered with any dnd observer.
- Optional.
- Default is
undefined.
container
WARNING
The core container setting is not available in useDraggable. It has been removed to prevent React unmount errors caused by the core library reparenting React-controlled DOM nodes during drag.
If you need to reparent elements during drag, use dragPreview: true combined with dragPreviewContainer instead. The drag preview system creates non-React proxy elements that can be safely reparented without conflicting with React's virtual DOM.
startPredicate
type startPredicate = (data: {
draggable: Draggable<S>;
sensor: S;
event: S['_events_type']['start'] | S['_events_type']['move'];
}) => boolean | undefined;Check the startPredicate core docs for more info.
- Optional.
- Default is
() => true.
elements
type elements = (data: {
draggable: Draggable<S>;
drag: DraggableDrag<S>;
}) => (HTMLElement | SVGSVGElement)[] | null;A function that should return all the elements you want to move during the drag. The function is called when the drag is starting so you can dynamically change the returned array of elements.
This pattern lends itself pretty handy in React as you can use refs within the function and dynamically get the current values of all the refs of elements you want to drag around.
IMPORTANT
The core Draggable class will modify the elements' inline transform styles. So it's important that you don't modify the elements' inline transform styles via React during the drag.
Check the elements core docs for more info.
- Optional.
- Default is
() => null.
frozenStyles
type frozenStyles = (data: {
draggable: Draggable<S>;
drag: DraggableDrag<S>;
item: DraggableDragItem<S>;
style: CSSStyleDeclaration;
}) => CSSProperties | (keyof CSSProperties)[] | null;IMPORTANT
The core Draggable class will modify the element's inline styles based on the frozenStyles setting. So it's important that you don't modify the element's inline styles via React during the drag, at least not the ones that are affected by the frozenStyles setting.
Check the frozenStyles core docs for more info.
- Optional.
- Default is
() => null.
applyPosition
type applyPosition = (data: {
draggable: Draggable<S>;
drag: DraggableDrag<S>;
item: DraggableDragItem<S>;
phase: DraggableApplyPositionPhase;
}) => void;Check the applyPosition core docs for more info.
- Optional.
- Default is a (very involved) function that applies the position, container offset, alignment offset and matrix transform offsets the element's transform property, while respecting the element's original transform and transform origin.
computeClientRect
type computeClientRect = (data: {
draggable: Draggable<S>;
drag: DraggableDrag<S>;
}) => Readonly<Rect> | null;Check the computeClientRect core docs for more info.
- Optional.
- Default is
({ drag }) => drag.items[0].clientRect || null.
positionModifiers
type positionModifiers = DraggableModifier<S>[];Check the positionModifiers core docs for more info.
- Optional.
- Default is
[].
sensorProcessingMode
type sensorProcessingMode = DraggableSensorProcessingMode;Check the sensorProcessingMode core docs for more info.
- Optional.
- Default is
'sampled'.
dndGroups
type dndGroups = Set<DraggableDndGroup> | undefined;Check the dndGroups core docs for more info.
- Optional.
- Default is
undefined(no groups, meaning the draggable won't match any droppables that use Set-based matching).
onPrepareStart
type onPrepareStart = (drag: DraggableDrag<S>, draggable: Draggable<S>) => void;Check the onPrepareStart core docs for more info.
- Optional.
- Default is
undefined.
onStart
type onStart = (drag: DraggableDrag<S>, draggable: Draggable<S>) => void;Check the onStart core docs for more info.
- Optional.
- Default is
undefined.
onPrepareMove
type onPrepareMove = (drag: DraggableDrag<S>, draggable: Draggable<S>) => void;Check the onPrepareMove core docs for more info.
- Optional.
- Default is
undefined.
onMove
type onMove = (drag: DraggableDrag<S>, draggable: Draggable<S>) => void;Check the onMove core docs for more info.
- Optional.
- Default is
undefined.
onEnd
type onEnd = (drag: DraggableDrag<S>, draggable: Draggable<S>) => void;Check the onEnd core docs for more info.
- Optional.
- Default is
undefined.
onDestroy
type onDestroy = (draggable: Draggable<S>) => void;Check the onDestroy core docs for more info.
- Optional.
- Default is
undefined.
Return Value
type returnValue = Draggable<S> | null;Returns the Draggable instance or null if there are no sensors ready yet.
Types
UseDraggableSettings
// Import
import type { UseDraggableSettings } from 'dragdoll-react';
// Interface
interface UseDraggableSettings<S extends Sensor = Sensor> extends Omit<
Partial<DraggableOptions<S>>,
'container'
> {
dndObserver?: DndObserver<any> | null;
dragPreview?: boolean;
dragPreviewContainer?: HTMLElement | (() => HTMLElement);
dragPreviewExitTimeout?: number;
}