Vue 3 Draggable Component
vue-draggable-next is a Vue 3 component that facilitates drag-and-drop functionality within web applications, building upon the robust Sortable.js library. It provides a touch-friendly, lightweight solution for reordering lists and elements, supporting both the Composition API and Options API. The current stable version is 2.3.0. The project maintains an active release cadence, with several updates and fixes in recent months (e.g., v2.2.1, v2.2.0, v2.1.0 since late 2020), indicating ongoing development and bug resolution. Key differentiators include its explicit support for Vue 3's Composition API, built-in TypeScript definitions, a small gzipped footprint (~7kb), and full compatibility with all underlying Sortable.js options, making it a flexible choice for various drag-and-drop scenarios like Kanban boards or reorderable lists.
Common errors
-
Property 'VueDraggableNext' does not exist on type 'typeof import("...")'cause Incorrect import statement; attempting a default import when it's a named export.fixChange `import VueDraggableNext from 'vue-draggable-next'` to `import { VueDraggableNext } from 'vue-draggable-next'`. -
Argument of type '(...)' is not assignable to parameter of type 'never'.
cause Often seen when TypeScript cannot infer the type of elements in the `v-model` list or within event handlers, particularly if `item-key` is missing or types are not explicitly defined.fixEnsure your list is strongly typed (e.g., `list = ref<Person[]>()`) and that `item-key` is correctly set, allowing TypeScript to better track item types. -
[Vue warn]: Injection "Symbol(component:setup-context)" not found.
cause This warning can occur when a component expected to be rendered within a Vue context is not, or when there are mismatched Vue versions in your project.fixVerify that `vue-draggable-next` is correctly installed and imported, and that your build setup is using a consistent Vue 3 version. Clear node_modules and reinstall dependencies.
Warnings
- breaking When migrating from `vue-draggable` (Vue 2) to `vue-draggable-next` (Vue 3), the Vue 2 lifecycle hooks like `beforeDestroy` have been renamed to `beforeUnmount` in Vue 3. Ensure your custom component logic adapts to these new names.
- gotcha The `item-key` prop is crucial for Vue's efficient re-rendering and tracking of list items, especially during drag-and-drop operations. Failing to provide a stable, unique `item-key` can lead to unexpected behavior, performance issues, or incorrect list state.
- gotcha Versions prior to 2.2.1 had issues with missing TypeScript definition files, leading to type errors or incomplete IntelliSense when used in TypeScript projects.
- gotcha For `v-model` to work correctly with drag-and-drop, Vue requires the underlying data to be reactive. While `vue-draggable-next` handles internal updates, ensure the list passed to `v-model` (or `list`) is a reactive reference.
Install
-
npm install vue-draggable-next -
yarn add vue-draggable-next -
pnpm add vue-draggable-next
Imports
- VueDraggableNext
import VueDraggableNext from 'vue-draggable-next'
import { VueDraggableNext } from 'vue-draggable-next' - draggable
import { VueDraggableNext as draggable } from 'vue-draggable-next'
Quickstart
<template>
<div class="drag-container">
<draggable
v-model="list"
group="people"
@change="onListChange"
item-key="id"
>
<template #item="{ element }">
<div class="drag-item">
{{ element.name }}
</div>
</template>
</draggable>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { VueDraggableNext } from 'vue-draggable-next'
interface Person {
id: number
name: string
}
const list = ref<Person[]>([
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
{ id: 3, name: 'Bob' }
])
const onListChange = (event: any) => {
console.log('List changed:', event)
}
</script>
<style scoped>
.drag-container {
min-height: 200px;
padding: 20px;
border: 1px dashed #ccc;
}
.drag-item {
padding: 10px;
margin: 5px 0;
background: #f0f0f0;
border-radius: 4px;
cursor: grab;
transition: background 0.2s;
}
.drag-item:hover {
background: #e0e0e0;
}
</style>