Vue 3 Smooth Drag and Drop
vue3-smooth-dnd is a specialized Vue 3 wrapper for the highly performant `smooth-dnd` library, providing components for intuitive drag-and-drop functionality within Vue applications. Currently at version 0.0.6, the package primarily focuses on maintaining compatibility with the underlying `smooth-dnd` library and fixing integration-specific bugs. While release cadence is not formally established, updates appear to be driven by reported issues. Its key differentiator lies in directly porting the well-regarded Vue 2 `vue-smooth-dnd` wrapper to the Vue 3 ecosystem, aiming for a seamless transition for developers familiar with the original. It offers a robust, highly customizable, and visually smooth solution for list reordering and item movement, distinguishing itself from simpler drag-and-drop solutions by leveraging `smooth-dnd`'s optimized animation and physics.
Common errors
-
TypeError: Cannot read properties of null (reading 'cancelDrop')
cause An internal reference within `smooth-dnd` or its Vue wrapper became null, possibly due to a race condition or an unmounted component during a drag operation.fixThis issue was specifically addressed in `v0.0.6`. Ensure you are on the latest patch version. If it persists, it may indicate a specific edge case in your component's lifecycle or complex nested drag-and-drop scenarios that need careful management of component unmounting and re-rendering. -
[Vue warn]: Failed to resolve component: container / [Vue warn]: Failed to resolve component: draggable
cause The `Container` or `Draggable` components were not properly registered or imported into the Vue component where they are being used.fixEnsure `import { Container, Draggable } from "vue3-smooth-dnd";` is present in your script and that `components: { Container, Draggable }` is correctly declared in your Vue component options.
Warnings
- gotcha The documentation states that 'All the documentation for the Vue 2 version works with this package version too!' While the core logic of `smooth-dnd` might be consistent, Vue 3 introduces significant changes (e.g., Composition API, global mounting, `v-model` behavior). Developers should be cautious when directly applying Vue 2 code patterns from the original wrapper's documentation without validating Vue 3 compatibility.
- breaking As a `0.0.x` version, the package API, while based on a stable Vue 2 predecessor, could still introduce minor breaking changes or unexpected behaviors in future patch or minor releases. Developers should pin to exact versions and thoroughly test updates.
- gotcha Interactions between `smooth-dnd`'s internal DOM manipulation and Vue's reactivity system can sometimes lead to unexpected issues if not handled correctly, especially when dynamically adding/removing items or complex nested structures. This can manifest as visual glitches or incorrect drag behavior.
Install
-
npm install vue3-smooth-dnd -
yarn add vue3-smooth-dnd -
pnpm add vue3-smooth-dnd
Imports
- Container
const { Container } = require('vue3-smooth-dnd')import { Container } from 'vue3-smooth-dnd' - Draggable
import Draggable from 'vue3-smooth-dnd'
import { Draggable } from 'vue3-smooth-dnd' - Container, Draggable
import * as DndComponents from 'vue3-smooth-dnd'
import { Container, Draggable } from 'vue3-smooth-dnd'
Quickstart
<template>
<div>
<span>Studio Ghibli Tier List</span>
<Container @drop="onDrop">
<Draggable v-for="(item, i) in items" :key="item.id">
<div>
{{i + 1}} -> {{item.data}}
</div>
</Draggable>
</Container>
</div>
</template>
<script>
import { Container, Draggable } from "vue3-smooth-dnd";
export default {
name: "app",
components: { Container, Draggable },
data() {
return {
items: [
{ id: 1, data: "Princess Mononoke" },
{ id: 2, data: "Spirited Away" },
{ id: 3, data: "My Neighbor Totoro" },
{ id: 4, data: "Howl's Moving Castle" }
]
};
},
methods: {
onDrop(dropResult){
this.items = this.applyDrag(this.items, dropResult);
},
applyDrag(arr, dragResult){
const { removedIndex, addedIndex, payload } = dragResult;
if (removedIndex === null && addedIndex === null) return arr;
const result = [...arr];
let itemToAdd = payload;
if (removedIndex !== null) {
itemToAdd = result.splice(removedIndex, 1)[0];
}
if (addedIndex !== null) {
result.splice(addedIndex, 0, itemToAdd);
}
return result;
}
}
}
</script>