Vue Dndrop
vue-dndrop is a JavaScript library providing intuitive Vue wrappers for implementing robust drag and drop functionalities within web applications. Currently at stable version 1.3.4, it offers features for reordering elements, transferring items between multiple containers, and defining custom drop rules. The library maintains a moderate release cadence, with a focus on stability and addressing common user experience issues. A key differentiator is its streamlined support for Vue 3.x, which was introduced and stabilized from versions 1.1.0/1.1.1 onwards (initially via an `@next` npm tag). This ensures seamless integration for modern Vue projects. It actively resolves challenges such as scroll blocking on touch devices during dragging and prevents errors from null container states, contributing to a more reliable and polished user experience compared to some alternative solutions.
Common errors
-
Scrolling is blocked on touch devices after completing a drag operation.
cause A known bug in `vue-dndrop` versions prior to 1.3.1 affecting how touch events and scroll locks were handled.fixUpdate `vue-dndrop` to version 1.3.1 or newer. Use `npm update vue-dndrop` or `npm install vue-dndrop@latest`. -
TypeError: Cannot read properties of null (reading 'value') or similar errors originating from a `Container` component.
cause A `Container` component received a `null` value in an older version of the library, which was not gracefully handled, leading to unhandled exceptions.fixUpgrade `vue-dndrop` to version 1.1.0 or newer. This version includes fixes for preventing errors when containers receive null values. Use `npm install vue-dndrop@latest`. -
TypeError: Cannot read properties of undefined (reading 'payload') in `onDrop` handlers or similar issues related to payload data.
cause The `getChildPayload` function might not be returning a valid object for the given index, or the `payload` property within the `dropResult` object is `undefined` (e.g., a drag operation was cancelled or initiated from an invalid source). Additionally, documentation for `getChildPayload` had incorrect links in older versions.fixVerify that your `getChildPayload` function always returns a valid object for every accessible index. In your `onDrop` handler, implement checks to ensure `payload` is not `undefined` or `null` before attempting to access its properties. Consult the updated documentation (from v1.3.1 onwards) for the correct usage of `getChildPayload` and the `dropResult` structure.
Warnings
- gotcha Vue 3.x support was initially introduced in versions 1.1.0/1.1.1 and required installing with the `@next` npm tag. While this support is now stable in main releases, users on older setups or following outdated documentation might still attempt to use `@next`. Always install the latest stable version for the best Vue 3 compatibility.
- breaking Versions prior to 1.3.1 suffered from a critical bug that caused scrolling to become blocked after a drag operation, particularly on touch devices. This significantly impacted usability.
- gotcha Older versions (specifically prior to 1.1.0, and 1.0.3 addressing some aspects) lacked robust error prevention when `Container` components unexpectedly received `null` values, potentially leading to runtime errors.
Install
-
npm install vue-dndrop -
yarn add vue-dndrop -
pnpm add vue-dndrop
Imports
- Container
import Container from 'vue-dndrop' // Incorrect: Not a default export const { Container } = require('vue-dndrop') // Incorrect: CommonJS syntax for a modern ESM-first libraryimport { Container } from 'vue-dndrop' - Draggable
import Draggable from 'vue-dndrop' // Incorrect: Not a default export const { Draggable } = require('vue-dndrop') // Incorrect: CommonJS syntax for a modern ESM-first libraryimport { Draggable } from 'vue-dndrop'
Quickstart
<template>
<div class="dnd-example">
<h2>My Draggable List</h2>
<Container @drop="onDrop" :get-child-payload="getChildPayload" group-name="items-group" class="smooth-dnd-container">
<Draggable v-for="item in items" :key="item.id">
<div class="draggable-item">
{{ item.text }}
</div>
</Draggable>
</Container>
<h3>Dropped Items (for demonstration)</h3>
<pre>{{ JSON.stringify(droppedItems, null, 2) }}</pre>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Container, Draggable } from 'vue-dndrop';
interface Item {
id: number;
text: string;
}
const items = ref<Item[]>([
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' },
{ id: 3, text: 'Item 3' }
]);
const droppedItems = ref<Item[]>([]);
const onDrop = (dropResult: { addedIndex: number | null; removedIndex: number | null; payload: Item }) => {
const { addedIndex, removedIndex, payload } = dropResult;
if (removedIndex !== null && addedIndex !== null) {
// Reordering within the same container
const [removed] = items.value.splice(removedIndex, 1);
items.value.splice(addedIndex, 0, removed);
} else if (addedIndex !== null && payload) {
// Item was dropped into this container (e.g., from another list or a new item)
const newItems = [...items.value];
newItems.splice(addedIndex, 0, payload);
items.value = newItems;
droppedItems.value.push(payload); // Log dropped item for demo
}
};
const getChildPayload = (index: number): Item => {
return items.value[index];
};
</script>
<style scoped>
.dnd-example {
font-family: Arial, sans-serif;
padding: 20px;
max-width: 600px;
margin: 0 auto;
border: 1px solid #eee;
border-radius: 8px;
background-color: #fff;
}
h2, h3 {
text-align: center;
margin-bottom: 20px;
color: #333;
}
.draggable-item {
padding: 15px;
margin-bottom: 10px;
background-color: #f9f9f9;
border: 1px solid #ddd;
border-radius: 4px;
cursor: grab;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
transition: background-color 0.2s ease;
}
.draggable-item:hover {
background-color: #f0f0f0;
}
.smooth-dnd-container {
min-height: 50px;
background-color: #eef;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
border: 1px dashed #ccc;
}
</style>