Back to Dnd Kit

useSortable

apps/docs/vue/composables/use-sortable.mdx

latest6.7 KB
Original Source

import {Story} from '/snippets/story.mdx'; import {CodeSandbox} from '/snippets/sandbox.mdx'; import {sortableStyles} from '/snippets/code.mdx';

<Story id="sortable-vertical-list--basic-setup" framework="vue" height="320" hero />

Usage

The useSortable composable combines draggable and droppable behavior with sorting logic. Import it from @dnd-kit/vue/sortable.

<CodeSandbox template="vue" files={{ 'src/App.vue': {code: appCode, active: true}, 'src/SortableItem.vue': {code: itemCode}, 'src/styles.css': {code: sortableStyles, hidden: true}, }} height={560} previewHeight={180} />

<Note> When passing props to `useSortable`, wrap reactive values in `computed()` to maintain reactivity. Plain values like `props.id` are read once during setup and won't update. </Note>

Without the move helper

The example above uses the move helper from @dnd-kit/helpers, a convenience function that takes your items and a drag event and returns a new array with the item moved to its new position. It supports flat arrays and grouped records, handles canceled drags, and works with optimistic sorting out of the box.

If you need more control over state updates, you can manage state manually using the isSortable type guard and the sortable properties (initialIndex, index).

With optimistic sorting enabled (the default), you only need to handle the dragEnd event:

vue
<script setup>
import { ref } from 'vue';
import { DragDropProvider } from '@dnd-kit/vue';
import { isSortable } from '@dnd-kit/vue/sortable';
import SortableItem from './SortableItem.vue';

const items = ref([1, 2, 3, 4]);

function onDragEnd(event) {
  if (event.canceled) return;

  const { source } = event.operation;

  if (isSortable(source)) {
    const { initialIndex, index } = source;

    if (initialIndex !== index) {
      const newItems = [...items.value];
      const [removed] = newItems.splice(initialIndex, 1);
      newItems.splice(index, 0, removed);
      items.value = newItems;
    }
  }
}
</script>

<template>
  <DragDropProvider @dragEnd="onDragEnd">
    <ul class="list">
      <SortableItem
        v-for="(id, index) in items"
        :key="id"
        :id="id"
        :index="index"
      />
    </ul>
  </DragDropProvider>
</template>
<Tip> You can call `event.preventDefault()` in a `@dragOver` handler to prevent the `OptimisticSortingPlugin` from optimistically updating for that specific event. This is useful when you want to conditionally block certain moves. </Tip> <Info> Learn more about [optimistic sorting, type guards, and manual state management](/concepts/sortable#optimistic-sorting) in the Sortable concepts page. </Info>

Input

All input properties accept plain values or Vue refs/getters (MaybeRefOrGetter).

<ParamField path="id" type="MaybeRefOrGetter<UniqueIdentifier>" required> A unique identifier for this sortable instance. </ParamField> <ParamField path="index" type="MaybeRefOrGetter<number>" required> The current index of this item in the sorted list. </ParamField> <ParamField path="element" type="MaybeRefOrGetter<HTMLElement | null>" required> A template ref pointing to the sortable element. </ParamField> <ParamField path="group" type="MaybeRefOrGetter<string>" optional> The group this sortable belongs to. Used for sorting across multiple lists. </ParamField> <ParamField path="handle" type="MaybeRefOrGetter<HTMLElement | null>" optional> A template ref for a drag handle. </ParamField> <ParamField path="accept" type="MaybeRefOrGetter<string | string[]>" optional> The types of draggable elements this sortable accepts. </ParamField> <ParamField path="type" type="MaybeRefOrGetter<string>" optional> The type of this sortable element. </ParamField> <ParamField path="plugins" type="MaybeRefOrGetter<PluginDescriptor[]>" optional> An array of plugin descriptors for per-entity plugin configuration. Use `Plugin.configure()` to create descriptors. For example, `Feedback.configure({ feedback: 'clone' })`. </ParamField> <ParamField path="transition" type="MaybeRefOrGetter<SortableTransition>" optional> Animation transition configuration for sort operations. </ParamField> <ParamField path="modifiers" type="MaybeRefOrGetter<Modifier[]>" optional> Modifiers to apply to this sortable instance. </ParamField> <ParamField path="sensors" type="MaybeRefOrGetter<Sensor[]>" optional> Sensors to use for this sortable instance. </ParamField> <ParamField path="collisionDetector" type="MaybeRefOrGetter<CollisionDetector>" optional> A custom collision detection algorithm. </ParamField> <ParamField path="collisionPriority" type="MaybeRefOrGetter<number>" optional> The collision priority of this sortable element. Higher values take precedence when multiple droppable elements overlap. </ParamField> <ParamField path="disabled" type="MaybeRefOrGetter<boolean>" optional> Whether the sortable is disabled. </ParamField> <ParamField path="data" type="MaybeRefOrGetter<Data>" optional> Custom data to attach to this sortable instance. </ParamField>

Output

<ResponseField name="isDragging" type="ComputedRef<boolean>"> Whether this element is currently being dragged. </ResponseField> <ResponseField name="isDropping" type="ComputedRef<boolean>"> Whether this element is in the process of being dropped. </ResponseField> <ResponseField name="isDragSource" type="ComputedRef<boolean>"> Whether this element is the source of the current drag operation. </ResponseField> <ResponseField name="isDropTarget" type="ComputedRef<boolean>"> Whether this element is currently a drop target. </ResponseField> <ResponseField name="sortable" type="ShallowReadonly<Ref<Sortable>>"> The underlying `Sortable` instance. </ResponseField>

export const itemCode = `

<script setup> import { ref, computed } from 'vue'; import { useSortable } from '@dnd-kit/vue/sortable'; const props = defineProps(['id', 'index']); const element = ref(null); const { isDragging } = useSortable({ id: computed(() => props.id), index: computed(() => props.index), element, }); </script> <template> <li ref="element" class="item"> Item {{ id }} </li> </template> `.trim();

export const appCode = `

<script setup> import { ref } from 'vue'; import { DragDropProvider } from '@dnd-kit/vue'; import { move } from '@dnd-kit/helpers'; import SortableItem from './SortableItem.vue'; import './styles.css'; const items = ref([1, 2, 3, 4]); function onDragEnd(event) { items.value = move(items.value, event); } </script> <template> <DragDropProvider @dragEnd="onDragEnd"> <ul class="list"> <SortableItem v-for="(id, index) in items" :key="id" :id="id" :index="index" /> </ul> </DragDropProvider> </template> `.trim();