Skip to main content
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

id
string | number
required
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>
  );
}
data
T
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'},
});
disabled
boolean
default:"false"
Whether this draggable is disabled. Disabled draggables cannot be dragged.
const {ref} = useDraggable({
  id: 'item',
  disabled: true,
});
sensors
Sensor[]
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
Modifier[]
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
Plugin[]
Plugins specific to this draggable. Applied in addition to provider-level plugins.
alignment
{x: number, y: number}
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>
);
draggable
Draggable<T>
The underlying Draggable instance. Access this for advanced use cases.
isDragging
boolean
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>
);
isDropping
boolean
Whether this element is in the dropping animation phase (after release, before settling).
isDragSource
boolean
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>;
}

Build docs developers (and LLMs) love