Skip to content

useDraggable

useDraggable is a core composable that transforms an element into a draggable item. It handles all the drag state management, events, and DOM interactions necessary for drag operations.

API Reference

Options

ts
useDraggable(options?: IUseDragOptions)

The options object can include the following properties:

PropertyTypeDescriptionRequired
idstring | numberUnique identifier for the draggable elementNo
groupsstring[]Groups this draggable belongs toNo
eventsObjectEvent handlers (see Events section)No
dataObjectCustom data to associate with draggable elementNo
keyboardObjectKeyboard navigation configurationNo
containerComponentCustom overlay componentNo
layerComponent | nullCustom layer componentNo
sensorObjectCustom sensor configurationNo

Events Object

EventTypeDescription
onStart(store: IDnDStore, payload: IDnDPayload) => voidCalled when drag operation starts
onMove(store: IDnDStore, payload: IDnDPayload) => voidCalled when draggable element is moving
onHover(store: IDnDStore, payload: IDnDPayload) => voidCalled when hovering over a target
onLeave(store: IDnDStore, payload: IDnDPayload) => voidCalled when leaving a target
onEnd(store: IDnDStore, payload: IDnDPayload) => voidCalled when drag operation ends

All event handlers receive the entire drag and drop store and a payload object as parameters.

Payload Object

The payload parameter provides access to all dragging elements:

PropertyTypeDescription
itemsIDraggingElement[]Array of all elements being dragged

Data Object

PropertyTypeDescription
sourceany[]Array reference holding the draggable item
indexnumberIndex of the item in the source array
[key]anyAny additional custom data

Keyboard Object

PropertyTypeDescription
moveStepnumberPixels to move when using keyboard navigation

Return Value

useDraggable returns an object with the following properties:

PropertyTypeDescription
elementRefRef<HTMLElement | null>Template ref to attach to the draggable element
isDraggingComputedRef<boolean>Whether this element is currently being dragged
isOveredComputedRef<boolean>Whether this element is being hovered over by a draggable
isAllowedComputedRef<boolean>Whether the current draggable can be dropped on this element
isLazyAllowedComputedRef<boolean>Similar to isAllowed, but only updates when hovering over the element
pointerPositionObjectCurrent and initial pointer positions
handleDragStartFunctionFunction to start the drag operation
idstring | numberUnique identifier for this draggable

Event Handling with Payload

You can use the payload parameter to easily access the dragged elements:

ts
const { elementRef } = useDraggable({
  id: 'my-draggable',
  data: computed(() => ({
    task: props.task,
  })),
  events: {
    onStart: (store, payload) => {
      // Access the first dragged element (most common scenario)
      const draggedElement = payload.items[0];
      console.log('Started dragging:', draggedElement.data);
    },
    onEnd: (store, payload) => {
      console.log('Number of items dragged:', payload.items.length);
    },
  },
});

Drag Start

To initiate dragging, bind the handleDragStart function to the appropriate event:

html
<div
  ref="elementRef"
  @pointerdown="handleDragStart"
>
  Drag me
</div>

For nested elements, use the .self modifier to prevent drag initiation from child elements:

html
<div
  ref="elementRef"
  @pointerdown.self="handleDragStart"
>
  <span>This won't trigger drag</span>
  <div>Neither will this</div>
</div>

Custom Drag Handles

Alternatively, you can implement custom drag handles for more explicit control:

html
<div ref="elementRef">
  <div
    class="drag-handle"
    @pointerdown="handleDragStart"
  >
    :: Drag here
  </div>
  <div class="content">
    <!-- Content that doesn't trigger dragging -->
  </div>
</div>

Keyboard Support

To support keyboard-based dragging (for accessibility), you can bind to keyboard events:

html
<div
  ref="elementRef"
  @pointerdown.self="handleDragStart"
  @keydown.space.self="handleDragStart"
  tabindex="0"
>
  Drag me with mouse or space key
</div>

The .self modifier works similarly with keyboard events, preventing nested focusable elements from triggering drag operations.

You can customize which keys trigger drag operations by changing the key modifier (e.g., .enter, .space).

Groups

The grouping system determines which drop zones a draggable element can interact with. Only drop zones that share at least one group with the draggable element will accept it.

ts
// A draggable that belongs to the 'fruits' group
const { elementRef } = useDraggable({
  groups: ['fruits'],
});

// A draggable that belongs to multiple groups
const { elementRef } = useDraggable({
  groups: ['documents', 'images'],
});

Important Notes

  1. The elementRef must be bound to your draggable element in the template.
  2. All event handlers receive the complete drag and drop store and a payload object as parameters.
  3. For more precise control over drag triggers, use Vue's event modifiers (.self, .stop, etc.).
  4. Always add tabindex="0" to draggable elements when implementing keyboard support.
  5. Use the isDragging computed value to apply visual feedback during drag operations.
  6. The isLazyAllowed property is useful for optimizing performance with complex validation logic, as it only updates when hovering:
html
<div
  ref="elementRef"
  class="draggable"
  :class="{
    'draggable--dragging': isDragging,
    'draggable--allowed': isLazyAllowed // Only updates on hover
  }"
  @pointerdown="handleDragStart"
>
  Drag me
</div>
  1. To ensure proper reactivity, especially for dynamic content, wrap the data property in a computed():

Released under the MIT License.