useDraggable
Creates and maintains a Draggable instance using Solid's lifecycle. The hook keeps the instance synchronized with sensors, options, and the current DndObserver context.
Usage
/** @jsxImportSource solid-js */
import { render } from 'solid-js/web';
import {
useDraggable,
useDraggableDrag,
useKeyboardMotionSensor,
usePointerSensor,
} from 'dragdoll-solid';
function Card() {
let element: HTMLDivElement | null = null;
const [pointerSensor, setPointerSensorRef] = usePointerSensor();
const [keyboardSensor, setKeyboardSensorRef] = useKeyboardMotionSensor();
const draggable = useDraggable([pointerSensor, keyboardSensor], () => ({
elements: () => (element ? [element] : []),
onStart: ({ sensor }) => {
console.log('drag started with', sensor?.constructor.name);
},
}));
const drag = useDraggableDrag(draggable);
const setRefs = (node: HTMLDivElement | null) => {
element = node;
setPointerSensorRef(node);
setKeyboardSensorRef(node);
};
return (
<div ref={setRefs} class={`card draggable ${drag() ? 'dragging' : ''}`} tabIndex={0}>
Drag me
</div>
);
}
render(() => <Card />, document.getElementById('root')!);Signature
function useDraggable<
E extends readonly (Accessor<Sensor | null> | null)[],
S extends Sensor = SensorsOf<E>,
>(
sensors: E,
settings?: MaybeAccessor<UseDraggableSettings<S> | undefined>,
): Accessor<Draggable<S> | null>;S is inferred from the sensors array, so you typically don't need to pass it explicitly:
- A single sensor type (e.g.
[pointerSensor]) narrowsSto that concrete class. - Mixed sensor arrays (e.g.
[pointerSensor, keyboardSensor]) widenSto a union of the passed sensor classes. - Empty arrays or all-
nullentries fall back to the defaultSensor.
Callbacks like startPredicate and modifiers like positionModifiers are typed against the inferred S, so event.startX (a PointerSensor-only field) is available without casts when the array contains only a pointer sensor.
Parameters
sensors
Array of sensor accessors (values returned from usePointerSensor, useKeyboardSensor, or useKeyboardMotionSensor). Each entry may also be a bare null or an Accessor that can resolve to null — the hook waits until at least one accessor yields a non-null sensor before creating the draggable. When the resolved sensor set changes, the draggable's sensors are updated in place; any in-progress drag is preserved.
settings
Configuration settings for the Draggable instance. Extends core DraggableOptions (excluding container) with additional Solid-specific settings.
Settings can be a plain object or a MaybeAccessor (function returning the settings object). When provided as a function, the hook reacts to changes automatically via Solid's fine-grained tracking.
Settings are always merged with the default settings and provided to the Draggable instance. This way there is no cumulative effect of settings changes over time — old settings are completely overridden by new settings.
Option objects are diffed via a deep comparison; only real changes call draggable.updateSettings. When dndGroups or computeClientRect change, the hook automatically triggers 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.
Use the <DragPreview> component to render dynamic visuals directly into the proxy element. By using proxies, you bypass Solid's DOM ownership constraints while preserving 60fps 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).
You can explicitly provide a DndObserver instance to register the draggable with. This setting takes precedence over the context observer.
Set to null to explicitly opt out of the automatic context observer registration.
- Optional.
- Default is
undefined.
container
WARNING
The core container setting is not available in useDraggable. It has been removed to prevent framework errors caused by the core library reparenting Solid-controlled DOM nodes during drag.
Solid maintains internal position tracking for all DOM nodes it manages. Moving a Solid-managed node to a different parent (reparenting) desynchronizes this internal state, causing removeChild errors, lost reactivity, and broken component behavior.
If you need to reparent elements during drag, use dragPreview: true combined with dragPreviewContainer instead. The drag preview system creates non-Solid proxy elements that can be safely reparented without conflicting with Solid's internal DOM tracking.
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.
In Solid, you can capture element references via let variables set through ref callbacks, and return them from within this function.
IMPORTANT
The core Draggable class will modify the elements' inline transform styles. Make sure you don't modify the elements' inline transform styles 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. Make sure you don't modify the element's affected inline styles during the drag.
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 to 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 = Accessor<Draggable<S> | null>;Returns an accessor to the Draggable instance, or null if there are no sensors ready yet. Access with draggable().
Types
UseDraggableSettings
// Import
import type { UseDraggableSettings } from 'dragdoll-solid';
// Interface
interface UseDraggableSettings<S extends Sensor = Sensor> extends Omit<
Partial<DraggableOptions<S>>,
'container'
> {
dndObserver?: DndObserver<any> | null;
dragPreview?: boolean;
dragPreviewContainer?: HTMLElement | (() => HTMLElement);
dragPreviewExitTimeout?: number;
}Extends the core DraggableOptions with Solid-specific fields. container is omitted because reparenting Solid-managed DOM is unsafe — use dragPreview if you need a separate render surface. Use this type when you want to extract your settings object into a reusable helper; for one-shot usage, pass the settings inline and let the hook infer S for you.
MaybeAccessor
// Import
import type { MaybeAccessor } from 'dragdoll-solid';
// Type
type MaybeAccessor<T> = T | Accessor<T>;A reactive input that may be either a plain value or a Solid Accessor returning that value. Every hook in this package accepts its settings as a MaybeAccessor<T>, which means you can pass a static object for one-shot configuration or a function to opt into fine-grained reactivity.
Notes
- The draggable is destroyed automatically when the component is disposed.
- Recreating sensors, changing the
id, or swapping the observer triggers a new instance. - Option objects are diffed via a deep comparison; only real changes call
draggable.updateSettings. - If no sensors are available, the draggable is removed until at least one sensor exists.
- SSR-safe: returns
() => nullon the server.