Skip to content

Events and composables

Drag-and-drop event payload

The Drag, Drop, and DropList lifecycle events use a DnDEventPayload. Import its TypeScript type from the package root:

ts
import type { DnDEventPayload } from 'vue-easy-dnd'

function onDrop (event: DnDEventPayload) {
  console.log(event.type, event.data)
}
PropertyTypeDescription
typestring | number | nullType assigned by the source Drag.
dataunknownData assigned by the source Drag.
position{ x: number, y: number } | nullPointer position in viewport/client coordinates.
topVue component instance or nullForemost participating Drop or DropList under the pointer.
previousTopVue component instance or nullPrevious top target. Present when the active target changes, including dragenter and dragleave payloads.
sourceVue component instance or nullDrag component where the operation began.
successboolean | nulltrue or false when an operation ends; otherwise normally null.
nativeEvent | nullMouse, touch, keyboard, cancellation, or other native event associated with the lifecycle update.

The payload also exposes sourceController, topController, and sometimes previousTopController. These are lower-level controller objects intended for custom wrappers built with useDrag and useDrop; normal application event handlers should use the component and data fields above.

DropList emits specialized event objects for list changes rather than DnDEventPayload:

  • InsertEvent contains type, data, and index.
  • ReorderEvent contains from, to, locked, and apply(array).

See the DropList event reference for examples.

Component event timing

EventEmitted byTiming
dragstartDragThe pointer has moved farther than delta after the gesture is initialized.
dragoverDrop, DropListThe pointer moves while that component is the foremost participating target.
dragenterDrop, DropListThat component becomes the foremost participating target.
dragleaveDrop, DropListThat component stops being the foremost participating target.
dropDrop, DropListA permitted operation is released over that target. For an external DropList insertion, this is emitted before insert.
dragendDrag, active Drop or DropListThe operation succeeds, is released elsewhere, is cancelled, or the source unmounts.
copy / cutDragA successful target uses the corresponding mode.
insertDropListExternal data is dropped at a calculated list index.
reorderDropListAn item from that same list is released at a different allowed index.

useDragAware

useDragAware() exposes the current global drag state as computed refs:

ts
import { useDragAware } from 'vue-easy-dnd'

const {
  dragInProgress,
  dragType,
  dragData,
  dragPosition,
  dragSource,
  dragTop
} = useDragAware()
Returned refDescription
dragInProgressWhether an operation is active.
dragTypeCurrent string, number, or null type.
dragDataCurrent source data.
dragPositionCurrent { x, y } viewport/client position or null.
dragSourceCurrent source Drag component instance or null.
dragTopForemost participating target component instance or null.

Vue unwraps these refs automatically in templates.

Observe drag state

useDragAware exposes reactive state without coupling your component to drag internals.

One
Two
Target
In progress
false
Type
Data
Position
Source component
Top component
View example code

Template

vue
<div class="dnd-demo__row">
  <Drag
    class="dnd-demo__item"
    type="number"
    :data="1"
  >
    One
  </Drag>
  <Drag
    class="dnd-demo__item"
    type="number"
    :data="2"
  >
    Two
  </Drag>
  <Drop class="dnd-demo__zone demo-state-target">
    Target
  </Drop>
</div>
<dl class="demo-state">
  <div><dt>In progress</dt><dd>{{ dragInProgress }}</dd></div>
  <div><dt>Type</dt><dd>{{ dragType ?? '—' }}</dd></div>
  <div><dt>Data</dt><dd>{{ dragData ?? '—' }}</dd></div>
  <div><dt>Position</dt><dd>{{ formattedPosition }}</dd></div>
  <div><dt>Source component</dt><dd>{{ dragSource ? 'available' : '—' }}</dd></div>
  <div><dt>Top component</dt><dd>{{ dragTop ? 'available' : '—' }}</dd></div>
</dl>

TypeScript

ts
import { computed } from 'vue';
import { Drag, Drop, useDragAware } from 'vue-easy-dnd';

const {
  dragInProgress,
  dragType,
  dragData,
  dragPosition,
  dragSource,
  dragTop
} = useDragAware();
const formattedPosition = computed(() => dragPosition.value
  ? `${Math.round(dragPosition.value.x)}, ${Math.round(dragPosition.value.y)}`
  : '—');

useDrag and useDrop

useDrag() and useDrop() power the built-in components and are exported for authors of custom drag/drop wrappers. They are lower-level than useDragAware: callers supply reactive props, an emit function, HTML element refs, and component-specific option callbacks.

ts
const drag = useDrag(props, emit, {
  rootElement,
  dragImageElement,
  hasDragImage: () => Boolean(slots['drag-image'])
})

const drop = useDrop(props, emit, {
  rootElement,
  dragImageElement
})

Both composables must be called during component setup(). Their root ref must resolve to an HTMLElement by the time the component mounts, and the caller is responsible for rendering the returned CSS state and any drag-image model. Prefer the built-in components unless you need a custom rendering contract.

Global dnd state

The exported dnd singleton contains the same reactive operation state and provides on/off lifecycle subscriptions:

ts
import { onBeforeUnmount } from 'vue'
import { dnd } from 'vue-easy-dnd'

const onStart = (event) => console.log(event.data)

dnd.on('dragstart', onStart)
onBeforeUnmount(() => dnd.off('dragstart', onStart))

Always remove subscriptions when their owner unmounts. For reactive UI state, prefer useDragAware().

Other advanced exports

  • DnDEvent provides an initialized event-shaped class, while component handlers receive objects matching DnDEventPayload and should not rely on instanceof DnDEvent.
  • InsertEvent and ReorderEvent are the concrete payload classes emitted by DropList.
  • refreshDragImage() rebuilds the image for the current operation after reactive slot content changes.
  • createDragImage(element) creates the positioned DOM clone used by the image system.
  • DragImagesManager is the underlying image lifecycle class. The package already creates the manager required for normal operation. If an advanced integration creates another instance, call its dispose() method when finished so its global subscriptions and image nodes are removed.
  • DragFeedback is the lightweight wrapper used internally to measure a DropList feedback slot. It remains exported for compatibility, but application components normally do not need it.