The useDraggable hook makes an element draggable. It registers the element with the drag and drop manager, attaches sensors, and provides reactive state about the drag status.
Usage
import {useDraggable} from '@dnd-kit/react';
function DraggableItem({id}) {
const {ref, isDragging} = useDraggable({id});
return (
<div ref={ref} style={{opacity: isDragging ? 0.5 : 1}}>
Drag me
</div>
);
}
Parameters
Unique identifier for this draggable element. Used to track the element during drag operations.const {ref} = useDraggable({id: 'item-1'});
element
Element | RefObject<Element>
The DOM element to make draggable. If not provided, use the ref from the return value.const elementRef = useRef(null);
useDraggable({id: 'item', element: elementRef});
handle
Element | RefObject<Element>
A specific element to use as the drag handle. If provided, dragging can only be initiated from this element.function DraggableWithHandle() {
const {ref, handleRef} = useDraggable({id: 'item'});
return (
<div ref={ref}>
<div ref={handleRef} style={{cursor: 'grab'}}>
⋮⋮ Drag handle
</div>
<div>Content</div>
</div>
);
}
Custom data to attach to this draggable. Accessible during drag operations and in event handlers.const {ref} = useDraggable({
id: 'item-1',
data: {label: 'First Item', category: 'A'},
});
Whether this draggable is disabled. Disabled draggables cannot be dragged.const {ref} = useDraggable({
id: 'item',
disabled: true,
});
Override the sensors for this specific draggable. By default, inherits from DragDropProvider.import {PointerSensor} from '@dnd-kit/react';
const {ref} = useDraggable({
id: 'item',
sensors: [new PointerSensor()],
});
Modifiers that transform the position during drag. Applied in addition to provider-level modifiers.import {restrictToVerticalAxis} from '@dnd-kit/modifiers';
const {ref} = useDraggable({
id: 'item',
modifiers: [restrictToVerticalAxis],
});
Plugins specific to this draggable. Applied in addition to provider-level plugins.
Alignment point for the drag source, relative to the element’s position. Values are percentages (0-1).// Align to center of element
const {ref} = useDraggable({
id: 'item',
alignment: {x: 0.5, y: 0.5},
});
Return Value
ref
(element: Element | null) => void
Callback ref to attach to the draggable element.const {ref} = useDraggable({id: 'item'});
return <div ref={ref}>Draggable</div>;
handleRef
(element: Element | null) => void
Callback ref to attach to a drag handle element.const {ref, handleRef} = useDraggable({id: 'item'});
return (
<div ref={ref}>
<div ref={handleRef}>⋮⋮</div>
</div>
);
The underlying Draggable instance. Access this for advanced use cases.
Whether this element is currently being dragged.const {ref, isDragging} = useDraggable({id: 'item'});
return (
<div ref={ref} style={{opacity: isDragging ? 0.5 : 1}}>
{isDragging ? 'Dragging...' : 'Drag me'}
</div>
);
Whether this element is in the dropping animation phase (after release, before settling).
Whether this element is the source of the current drag operation (true during dragging and dropping).
Examples
Basic Draggable
function BasicDraggable() {
const {ref, isDragging} = useDraggable({id: 'basic'});
return (
<div
ref={ref}
style={{
padding: '20px',
border: '2px solid black',
opacity: isDragging ? 0.5 : 1,
cursor: 'grab',
}}
>
Drag me
</div>
);
}
Draggable with Custom Data
interface ItemData {
label: string;
category: string;
}
function DraggableItem({id, label, category}: ItemData & {id: string}) {
const {ref, isDragging} = useDraggable<ItemData>({
id,
data: {label, category},
});
return (
<div ref={ref} className={isDragging ? 'dragging' : ''}>
<h3>{label}</h3>
<span>{category}</span>
</div>
);
}
Draggable with Handle
function DraggableCard() {
const {ref, handleRef, isDragging} = useDraggable({id: 'card'});
return (
<div ref={ref} className="card">
<div
ref={handleRef}
style={{
cursor: isDragging ? 'grabbing' : 'grab',
padding: '10px',
background: '#f0f0f0',
}}
>
⋮⋮ Drag here
</div>
<div style={{padding: '20px'}}>
<h3>Card Content</h3>
<p>This content is not draggable, only the handle above.</p>
</div>
</div>
);
}
Conditionally Disabled
function ConditionalDraggable({id, locked}) {
const {ref, isDragging} = useDraggable({
id,
disabled: locked,
});
return (
<div
ref={ref}
style={{
opacity: locked ? 0.5 : isDragging ? 0.7 : 1,
cursor: locked ? 'not-allowed' : 'grab',
}}
>
{locked ? '🔒 Locked' : 'Drag me'}
</div>
);
}
With Custom Modifiers
import {restrictToHorizontalAxis} from '@dnd-kit/modifiers';
function HorizontalDraggable() {
const {ref} = useDraggable({
id: 'horizontal',
modifiers: [restrictToHorizontalAxis],
});
return (
<div ref={ref} style={{padding: '20px', border: '2px solid blue'}}>
Can only drag horizontally
</div>
);
}
Type Safety
Use TypeScript generics to type the custom data:
interface TaskData {
title: string;
priority: 'low' | 'medium' | 'high';
assignee: string;
}
function DraggableTask({task}: {task: TaskData & {id: string}}) {
const {ref, isDragging} = useDraggable<TaskData>({
id: task.id,
data: {
title: task.title,
priority: task.priority,
assignee: task.assignee,
},
});
return <div ref={ref}>{task.title}</div>;
}