Docs/API

Modifiers and collision

Functions that change where the dragged item goes, and those that choose the target under it.

Live

Loading the demo…

<script setup lang="ts">
import { ref } from 'vue';
import { DnDProvider, closestCenter, closestCorners, pointerFirst, pointerWithin, rectIntersection } from '@vue-dnd-kit/core';
import ModifierLab from './ModifierLab.vue';

// Collision detection is the provider's: it picks the target, here one of the two pads.
const COLLISIONS = { pointerFirst, pointerWithin, rectIntersection, closestCenter, closestCorners };
const collision = ref<keyof typeof COLLISIONS>('pointerFirst');
</script>

<template>
  <DnDProvider :collision="COLLISIONS[collision]">
    <div class="demo">
      <code class="call">&lt;DnDProvider :collision="{{ collision }}"&gt;</code>
      <div class="collisions" role="radiogroup" aria-label="collision">
        <label v-for="name in Object.keys(COLLISIONS)" :key="name" :class="{ on: collision === name }">
          <input v-model="collision" type="radio" :value="name" />
          {{ name }}
        </label>
      </div>
      <ModifierLab />
    </div>
  </DnDProvider>
</template>

<style scoped>
.demo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.collisions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 2px;
  max-width: 100%;
  padding: 3px;
  border-radius: 9px;
  background: color-mix(in srgb, currentColor 7%, transparent);
}

.call {
  font: 11px/1.4 ui-monospace, Menlo, Consolas, monospace;
  color: #42b883;
}

label {
  padding: 4px 8px;
  border-radius: 6px;
  font: 11px/1.3 ui-monospace, Menlo, Consolas, monospace;
  cursor: pointer;
}

label.on {
  background: Canvas;
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.25);
  font-weight: 600;
}

label:has(input:focus-visible) {
  outline: 2px solid #42b883;
}

input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
</style>

Modifiers

import { restrictToParent, snapToGrid } from '@vue-dnd-kit/core';

makeSortableList(list, { items, modifiers: [restrictToParent] });
makeDragArea(board, { modifiers: [snapToGrid({ x: 24, y: 24, smooth: true })] });

A modifier takes the drag's offset and returns a new one. They run in order: the provider's modifiers, a drag area's, the draggable's own (or its list's), then the area's bounds (restrictToArea), and the axis lock last. See Axes, bounds and grids.

Modifier

Moves only vertically: horizontally it stays with the content it started in (containerShift), however the page scrolls. The axis: 'y' option does the same, last.

Modifier

Moves only horizontally: vertically it stays with the content it started in (containerShift), however the page scrolls. The axis: 'x' option does the same, last.

Modifier

Keeps the overlay inside the window.

Modifier

Keeps the overlay inside the container the drag started in: the sortable list (not its DOM parent) or, for plain draggables, the parent element.

(target: MaybeElementRef) => Modifier

Keeps the overlay inside an element: the element, a ref or a getter.

Modifier

Keeps the overlay inside the first scrollable ancestor of the dragged element.

(size: number | SnapToGridOptions) => Modifier

Snaps the overlay to a grid, to the nearest cell: a number is the size of square cells; options set each axis, where the grid starts and a glide between cells (SnapToGridOptions).

Modifier

Centers the overlay under the pointer instead of keeping the grab offset.

Options of snapToGrid.

x?
number

The cell's width in px. Without one (or 0), the drag moves freely along x.

y?
number

The cell's height in px. Without one (or 0), the drag moves freely along y: { y: 15 } snaps the time of a calendar event only.

'container' | 'delta'

'container' (default): the overlay's top-left lands on grid lines drawn from the container's top-left (containerRect: the sortable list or the parent element). 'delta': the drag offset is rounded, keeping where the item started off-grid.

boolean | AnimationOptions

Glide the overlay between snapped positions (default 80ms). Collision still uses the snapped position at once.

Changes where the dragged item goes: takes the drag's offset (transform) and returns a new one.

type Modifier = (args: ModifierArgs) => Coordinates

What a modifier gets, on every move of the drag.

Coordinates

Offset from the drag start, as modified by the previous modifiers.

Active

The drag.

Rect

Rect of the dragged element at drag start.

Rect

Rect of the overlay box if it were moved by transform, at the size it has now. With overlayFit: 'slot' the box resizes around the pointer, so it isn't always activeRect moved by transform.

Coordinates

Pointer position at drag start.

Rect | null

Sortable list container (or parent element) of the dragged item, where it is now (it follows scrolling).

Coordinates

How far the content the drag started in has moved on screen since the start: the container moved (the page or a container around it scrolled, the layout shifted), and its content scrolled inside it. Axis locks hold the other axis there, so the drag stays on its row or column of that content.

Rect

Rect of the window (the viewport).

Over | null

The drop target under the drag, as last found; null over nothing.

Collision detection

import { closestCorners } from '@vue-dnd-kit/core';
<DnDProvider :collision="closestCorners" />

The provider's collision picks the target; the best candidate comes first.

CollisionDetection

Default: what is under the pointer, else what the overlay overlaps.

CollisionDetection

Targets under the pointer, from hit-testing: respects z-order, overlays and shadow roots.

CollisionDetection

Targets overlapping the overlay: higher priority, then deeper nesting, then intersection over union.

CollisionDetection

Every target, by distance between centers.

CollisionDetection

Every target, by summed distance between corners (good for stacked targets such as kanban columns).

Returns candidate ids, best first.

type CollisionDetection = (args: CollisionArgs) => Key[]

What a collision detection gets, on every move of the drag.

Coordinates

Pointer in viewport coordinates, after modifiers.

Rect | null

Rect of the dragged box (the overlay, or the element itself with overlay: false) where the drag has moved it.

CollisionCandidate[]

Every drop target the drag could go to, whether its accept rule takes the drag or not.

Key[]

Drop targets under the pointer from hit-testing (respects z-order and shadow roots), best first.

A drop target a collision detection can pick: enabled, on the page and not fully clipped.

Key

Id of the drop target.

Rect

Visible part of the target: clipped by its scroll containers and the viewport.

number

The target's priority (default 0): higher wins when targets overlap without nesting. Sortable items beat containers.

number

Nesting level among drop targets: 0 at the top, deeper targets have higher values.