apps/docs/vue/composables/use-sortable.mdx
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 />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>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:
<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>
All input properties accept plain values or Vue refs/getters (MaybeRefOrGetter).
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();