Vue 3 drag-and-drop component based on Sortable.js - Touch-friendly, lightweight, and TypeScript ready
npm install vue-draggable-next


🎯 Vue 3 drag-and-drop component based on Sortable.js
✨ Features:
- 🚀 Vue 3 Composition API support
- 📱 Touch-friendly (mobile support)
- 🎨 No CSS framework dependency
- 📦 TypeScript definitions included
- ⚡ Lightweight (~7kb gzipped)
- 🔧 All Sortable.js options supported
📚 Live Demo & Playground | 📖 Migration Guide | 🎯 Examples
``bashnpm
npm install vue-draggable-next
🚀 Quick Start
$3
`vue
v-model="list"
group="people"
@change="onListChange"
item-key="id"
>
{{ element.name }}
`$3
`vue
:list="list"
class="drag-area"
@change="handleChange"
>
v-for="element in list"
:key="element.id"
class="drag-item"
>
{{ element.name }}
`
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| modelValue | Array | [] | Array to be synchronized with drag-and-drop (use with v-model) |list
| | Array | [] | Alternative to modelValue, directly mutates the array |itemKey
| | String\|Function | undefined | Key to use for tracking items (recommended for better performance) |tag
| | String | 'div' | HTML tag for the root element |component
| | String | null | Vue component name to use as root element |componentData
| | Object | null | Props/attrs to pass to the component |clone
| | Function | (item) => item | Function to clone items when dragging |move
| | Function | null | Function to control move operations |group
| | String\|Object | undefined | Sortable group options |sort
| | Boolean | true | Enable sorting within the list |disabled
| | Boolean | false | Disable drag and drop |animation
| | Number | 0 | Animation speed (ms) |ghostClass
| | String | '' | CSS class for the ghost element |chosenClass
| | String | '' | CSS class for the chosen element |dragClass
| | String | '' | CSS class for the dragging element |
| Event | Description | Payload |
|-------|-------------|---------|
| @change | Fired when the list changes | { added?, removed?, moved? } |@start
| | Dragging started | SortableEvent |@end
| | Dragging ended | SortableEvent |@add
| | Item added from another list | SortableEvent |@remove
| | Item removed to another list | SortableEvent |@update
| | Item order changed | SortableEvent |@sort
| | Any change to the list | SortableEvent |@choose
| | Item is chosen | SortableEvent |@unchoose
| | Item is unchosen | SortableEvent |
`vue
Todo
group="tasks"
class="drag-area"
:animation="150"
>
v-for="item in todoList"
:key="item.id"
class="task-item"
>
{{ item.text }}
Done
group="tasks"
class="drag-area"
:animation="150"
>
v-for="item in doneList"
:key="item.id"
class="task-item done"
>
{{ item.text }}
`
`vue
handle=".drag-handle"
:animation="200"
>
v-for="item in list"
:key="item.id"
class="item-with-handle"
>
⋮⋮
{{ item.name }}
`
`vue
tag="transition-group"
:component-data="{
tag: 'div',
type: 'transition',
name: 'fade'
}"
:animation="200"
>
v-for="item in list"
:key="item.id"
class="fade-item"
>
{{ item.text }}
`
`typescript
// types.ts
export interface DraggableItem {
id: string | number
[key: string]: any
}
export interface DragChangeEvent
added?: {
newIndex: number
element: T
}
removed?: {
oldIndex: number
element: T
}
moved?: {
newIndex: number
oldIndex: number
element: T
}
}
`
`vue
@change="onListChange"
item-key="id"
>
v-model="element.completed"
type="checkbox"
>
{{ element.text }}
`
`vue
Source (Clone)
:group="{ name: 'shared', pull: 'clone', put: false }"
:clone="cloneItem"
:sort="false"
>
{{ item.name }}
Target
group="shared"
>
{{ item.name }}
`
`vue
:move="checkMove"
>
v-for="item in list"
:key="item.id"
:class="{ locked: item.locked }"
class="move-item"
>
{{ item.name }}
🔒
`
If you're migrating from the Vue 2 version, here are the key changes:
vue
{{ item.name }}
`$3
`vue
{{ element.name }}
{{ item.name }}
`$3
- Vue 3 required: This package only works with Vue 3
- Composition API: Full support for