Vue Smooth DnD
Vue Smooth DnD is a lightweight and performant Vue.js wrapper library for the underlying `smooth-dnd` core library. Currently at version 0.8.1, it provides comprehensive drag-and-drop and sortable functionalities for Vue applications. The library exposes Vue components like `Container` and `Draggable`, which allow developers to easily implement interactive UIs with features such as vertical or horizontal orientation, different drag behaviors (move, copy, drop-zone), drag handles, and auto-scrolling. While a specific release cadence isn't detailed, its evolution is closely tied to the `smooth-dnd` project. It stands out by offering a highly configurable, efficient solution, making it suitable for a wide array of user interaction requirements within the Vue ecosystem.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'removedIndex')
cause The `onDrop` event handler is attempting to access properties of `dropResult` (e.g., `removedIndex`, `addedIndex`) but `dropResult` is undefined or not an object due to an incorrect event binding or prop usage.fixVerify that the `@drop` event is correctly bound to the `Container` component and that the `onDrop` method receives the `dropResult` object as its argument. Ensure `dropResult` is not accidentally reassigned or ignored. -
Items do not move or reorder when dragged, or disappear after dropping.
cause The component's internal `items` array is not being updated correctly after a drop operation, likely because the `onDrop` event handler is missing or incorrectly implemented.fixEnsure your `onDrop` method correctly processes the `dropResult` by using a utility like `applyDrag` to create a *new* array with the updated order/content and assigns it back to your reactive `items` data property (`this.items = newItemsArray`). Direct mutation of the original array might not trigger reactivity in some Vue versions or setups. -
console.warn: [Vue warn]: Failed to resolve component: draggable
cause The `Draggable` component (or `Container`) has not been correctly imported or registered within the Vue component where it's being used.fixEnsure you have `import { Container, Draggable } from 'vue-smooth-dnd';` at the top of your script, and that both `Container` and `Draggable` are listed in the `components` option of your Vue component: `components: { Container, Draggable }`.
Warnings
- gotcha The `onDrop` event emitted by the `Container` component provides `dropResult` but does not automatically modify your data array. You must implement logic (like the provided `applyDrag` helper) to update your component's state based on `removedIndex`, `addedIndex`, and `payload`.
- gotcha All direct children of a `Container` component intended to be draggable must be explicitly wrapped within a `<Draggable>` component. Failing to do so will prevent items from being draggable or cause unexpected rendering issues.
- gotcha For drag-and-drop operations between multiple `Container` components, you must assign the same `group-name` prop to all containers that should participate in the group. If `group-name` is not set, a container will not accept drags from outside its own instance.
- gotcha The `tag` property for the `Container` component can accept either a `string` (e.g., `'div'`, `'ul'`) or an `object` with `value` and `props` (e.g., `{ value: 'table', props: { class: 'my-table' } }`). Misunderstanding this can lead to incorrect root element rendering.
Install
-
npm install vue-smooth-dnd -
yarn add vue-smooth-dnd -
pnpm add vue-smooth-dnd
Imports
- Container
import Container from 'vue-smooth-dnd';
import { Container } from 'vue-smooth-dnd'; - Draggable
const { Draggable } = require('vue-smooth-dnd');import { Draggable } from 'vue-smooth-dnd'; - Container, Draggable
import * as Components from 'vue-smooth-dnd';
import { Container, Draggable } from 'vue-smooth-dnd';
Quickstart
<template>
<div>
<div class="simple-page">
<Container @drop="onDrop">
<Draggable v-for="item in items" :key="item.id">
<div class="draggable-item">
{{item.data}}
</div>
</Draggable>
</Container>
</div>
</div>
</template>
<script lang="ts">
import { Container, Draggable } from "vue-smooth-dnd";
// Assume applyDrag and generateItems are utility functions provided by the user or an example
// For a runnable example, we'll define simple versions.
function applyDrag(arr: any[], dragResult: any) {
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;
}
function generateItems(count: number, creator: (index: number) => any) {
const result = [];
for (let i = 0; i < count; i++) {
result.push(creator(i));
}
return result;
}
export default {
name: "SimpleDnD",
components: { Container, Draggable },
data() {
return {
items: generateItems(5, i => ({ id: i, data: "Draggable " + i }))
};
},
methods: {
onDrop(dropResult: any) {
this.items = applyDrag(this.items, dropResult);
}
}
};
</script>
<style>
.draggable-item {
padding: 10px;
margin-bottom: 5px;
background-color: #f0f0f0;
border: 1px solid #ccc;
cursor: grab;
}
.simple-page {
width: 300px;
border: 1px dashed #eee;
padding: 10px;
}
</style>