Docs/API

Animations

The options and hooks of the animations — sorting, the drop, and changes no drag made.

The provider's animation (layout moves while sorting), dropAnimation (the overlay landing) and animateChanges (data changes no drag made) take false, options, or your own function — for GSAP or motion. Lists and trees can override animation and animateChanges; draggables, items and lists can bring their own dropAnimation, and drop targets an overDropAnimation. See Animations.

<DnDProvider :animation="{ duration: 180, easing: 'ease-out' }" :drop-animation="false" />

Each lane below is a provider of its own that gives animation, dropAnimation and animateChanges the same kind of value (animateChanges takes a function in move).

Live

Loading the demo…

<script setup lang="ts">
import { ref } from 'vue';
import { DnDProvider, type AutoAnimation, type DropAnimation, type LayoutAnimation } from '@vue-dnd-kit/core';
import AnimLane from './AnimLane.vue';
import { tiltDrop, tiltMove } from './tilt';

const SLOW = { duration: 700, easing: 'ease-in-out' };

// One shape per lane, for all three options: sorting, the drop, and changes no drag made.
const lanes: { label: string; animation: LayoutAnimation; dropAnimation: DropAnimation; animateChanges: AutoAnimation }[] = [
  { label: 'true', animation: true, dropAnimation: true, animateChanges: true },
  { label: 'false', animation: false, dropAnimation: false, animateChanges: false },
  { label: '{ duration: 700 }', animation: SLOW, dropAnimation: SLOW, animateChanges: SLOW },
  // `animateChanges` takes no function itself: its parts (`move`, `enter`, `leave`) do.
  { label: 'ƒ tilt', animation: tiltMove, dropAnimation: tiltDrop, animateChanges: { move: tiltMove } },
];

const letters = ['A', 'B', 'C', 'D'];
const rows = ref(lanes.map(() => letters.map((id, i) => ({ id, hue: 150 + i * 40 }))));

/** The same new order in every lane (Fisher–Yates: every order as likely): `animateChanges` plays it four ways. */
const shuffle = () => {
  const order = [...letters];
  for (let i = order.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [order[i], order[j]] = [order[j]!, order[i]!];
  }
  rows.value = rows.value.map((lane) => order.map((id) => lane.find((row) => row.id === id)!));
};
</script>

<template>
  <div class="demo">
    <code>&lt;DnDProvider :animation="shape" :drop-animation="shape" :animate-changes="shape"&gt;</code>
    <div class="lanes">
      <!-- A provider per lane: each one has its own animations. -->
      <DnDProvider
        v-for="(lane, index) in lanes"
        :key="lane.label"
        :animation="lane.animation"
        :drop-animation="lane.dropAnimation"
        :animate-changes="lane.animateChanges"
      >
        <AnimLane v-model="rows[index]!" :label="lane.label" />
      </DnDProvider>
    </div>
    <button type="button" @click="shuffle">Shuffle all</button>
  </div>
</template>

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

code {
  max-width: 100%;
  font: 11px/1.4 ui-monospace, Menlo, Consolas, monospace;
  text-align: center;
  opacity: 0.7;
}

button {
  padding: 6px 12px;
  border: 0;
  border-radius: 8px;
  background: #42b883;
  color: #fff;
  font: 600 12px/1 system-ui, sans-serif;
  cursor: pointer;
}

button:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

.lanes {
  display: grid;
  grid-template-columns: repeat(4, 128px);
  gap: 8px;
}

@media (max-width: 560px) {
  .lanes {
    grid-template-columns: repeat(2, 128px);
  }
}
</style>

Options

The timing of an animation. What is left out keeps the default of that animation.

number

In ms. Default 200 for layout moves, 250 for the drop; 0 turns the animation off.

string

A CSS easing ('ease-out', cubic-bezier(…)).

Sorting

How items move to their new places: true (default timing), options, false (they jump), or a function that animates one item from from to to and returns its Animation, a promise that settles when it ends, or nothing.

type LayoutAnimation = | boolean | AnimationOptions | ((context: LayoutAnimationContext) => Animation | Promise<unknown> | void)

What a layout animation function gets, for one item that moves to its new place.

Key

Key of the item.

HTMLElement

The item's element, already in its new place.

Rect

Visual rect before the change (where a running animation showed the item).

Rect

Layout rect after the change.

Required<AnimationOptions>

The timing the options give, defaults filled in.

'drag' | 'change'

What moved the item: a drag ('drag': sorting, the drop, a cancel), or a change of your data that no drag made ('change', with the list's animateChanges on).

AbortSignal

Aborted when the library interrupts the animation: the item moves again before it ends (the next animation starts where it is shown), or animations are turned off. A returned Animation is cancelled for you. Stop anything else here and undo the styles it set, at once: the layout is measured right after.

boolean

prefers-reduced-motion: reduce is set. The library's own animations don't run then; functions of yours always do, and decide with this (skip, or keep a fade).

The drop

How the overlay lands on a drop and flies back on a cancel: true (default timing), options, false (no flight: it ends where it was let go), or a function that animates overlay from from to to and returns its Animation, a promise that settles when it ends, or nothing.

type DropAnimation = | boolean | AnimationOptions | ((context: DropAnimationContext) => Animation | Promise<unknown> | void)

What a drop animation function gets: the overlay, where it is and where it lands.

HTMLElement

The overlay box; it lands on the active item's slot. In a multi-drag the clones of the other items riding inside it (the default overlay's, the DragPreviews of a custom one) land on their own slots by themselves (with the default timing when this is a custom function).

Rect

Where the overlay is when the drag ends.

Rect | null

Final rect of the dropped item; null when it isn't rendered or visible (the overlay fades out).

Rect | null

The item isn't shown after a drop on a zone (not a sortable list), e.g. it was dropped into another item (a folder) or a trash zone: the zone's visible rect. The default animation shrinks the overlay into it as it fades out, instead of fading in place.

'drop' | 'cancel'

'drop': the overlay lands where the drop put the item. 'cancel': it flies back to where the item was.

Active

The drag that ended.

Over | null

The target the drop lands on (a list, a zone); null on a cancel.

Required<AnimationOptions>

The timing the options give, defaults filled in.

boolean

prefers-reduced-motion: reduce is set. The library's own animations don't run then; functions of yours always do, and decide with this (skip, or keep a fade).

Data changes

Animates changes of a list's (or tree's) items that no drag made: items added, removed, moved (a sort, a shuffle, a move between lists of one group) or filtered. Off by default (false); true animates moves, entering and leaving items with the default timing. Drags keep their own animations (animation, dropAnimation). With prefers-reduced-motion: reduce the default animations don't run; functions of yours still do (with reducedMotion: true). Only items near the visible area animate (unmounted rows of a virtual list are skipped).

type AutoAnimation = boolean | AutoAnimationOptions

animateChanges with its parts: duration and easing for all of them, and per kind of change true, options (over the shared ones), false, or a function of your own.

LayoutAnimation

Items that changed places (the default FLIP, as animation's; a function replaces it).

EnterAnimation

Items that appeared. Default: they fade in where they are.

LeaveAnimation

Items that left. Default: a copy of the item fades out where it was, shrinking a little.

true, options, false (the item just appears), or a function that animates it in.

type EnterAnimation = | boolean | AnimationOptions | ((context: EnterAnimationContext) => Animation | Promise<unknown> | void)

An item that appeared in a list with animateChanges on: added, or shown again (a filter, an expanded tree node).

Key

Key of the item.

HTMLElement

The item's element, already in its place.

Rect

Where it appeared (layout rect).

Required<AnimationOptions>

The timing the options give, defaults filled in.

boolean

prefers-reduced-motion: reduce is set. The library's own animations don't run then; functions of yours always do, and decide with this (skip, or keep a fade).

true, options, false (the item just disappears), or a function that animates out the copy left in its place: return its Animation or a promise, and the copy is removed when it ends (at once when the function returns nothing).

type LeaveAnimation = | boolean | AnimationOptions | ((context: LeaveAnimationContext) => Animation | Promise<unknown> | void)

An item that left a list with animateChanges on: removed, or hidden (a filter, a collapsed tree node).

Key

Key of the item.

HTMLElement

A copy of the item's element, shown where the item was (the item itself is gone): positioned absolutely in its old parent, ignoring the pointer, marked data-dnd-leaving. The library removes it when your animation ends.

Rect

Where the item was.

Required<AnimationOptions>

The timing the options give, defaults filled in.

boolean

prefers-reduced-motion: reduce is set. The library's own animations don't run then; functions of yours always do, and decide with this (skip, or keep a fade).