Drag and Drop in Vue 3: Native, vue-draggable-plus, and Pragmatic Compared

Drag and drop looks trivial until you build it. Reordering a list is one problem, moving a card between two columns is another, and dropping a file into an upload zone is a third. Vue 3 gives you more than one way to handle all of them, and the right choice depends on what you are building.
This guide covers the three approaches worth knowing in 2026: the browser's native HTML5 drag-and-drop API, SortableJS wrappers like vue-draggable-plus, and Atlassian's pragmatic-drag-and-drop. You get a live interactive demo you can drag right now, a straight comparison of all three approaches, and an opinionated recommendation for each use case. If you only want the short answer, start with the decision table below, then jump to the approach that fits.
Interactive Live Demo: Vue 3 Drag and Drop
Try out the live Vue 3 Kanban board below powered by vue-draggable-plus. Reorder items within a column or drag cards across columns to observe real-time array state updates:
The Three Vue 3 Drag and Drop Approaches, Compared
| Approach | Best For | Dependency Weight | Touch & Mobile Support | Accessibility (a11y) | Setup Effort |
|---|---|---|---|---|---|
vue-draggable-plus (SortableJS) |
Sortable lists, reordering, Kanban boards, multi-list dragging | Small (~10KB) | Excellent (built-in touch fallback) | Good base (extensible via ARIA) | Low |
| Native HTML5 API | File drop zones, simple one-off drag targets | None (Browser Native) | Poor out of the box (requires custom touch listeners) | Requires manual keyboard/ARIA wiring | Low to write, high to polish |
pragmatic-drag-and-drop (Atlassian) |
Complex enterprise boards, strict accessibility, high-performance UIs | Modular (Lightweight core) | Excellent (native touch support) | Industry-leading (built-in a11y primitives) | Medium to High |
Note: vue-draggable-plus is the actively maintained Vue 3 Composition API successor to vuedraggable. While both wrap SortableJS, vue-draggable-plus provides native Vue 3 script setup macros, TypeScript support, and composable bindings.
Opinionated Recommendations
Reordering a list or building a Kanban board? Reach for
vue-draggable-plus. It wraps SortableJS, binds directly to reactive Vue arrays viav-model, and handles touch devices without extra configuration. It is the fastest path to a production-ready sortable UI.Building a file drop zone or a simple one-off drag target? Use the native HTML5 Drag and Drop API. It requires zero dependencies and a basic drop target takes just a few Vue event handlers (
@dragover.prevent,@drop). Do not pull in an external library for file uploads.Building an enterprise application with strict accessibility, nested lists, or high performance demands? Choose
pragmatic-drag-and-dropby Atlassian. It is framework-agnostic, headless, and provides robust accessibility primitives used in Jira and Trello.
Approach 1: vue-draggable-plus (Recommended for Lists & Kanban Boards)
vue-draggable-plus is the modern Vue 3 successor to the legacy vuedraggable library. Powered by SortableJS, it provides smooth animations, touch support, and seamless v-model binding with Vue 3 <script setup>.
Step 1: Install vue-draggable-plus
Install the package via npm:
npm install vue-draggable-plusStep 2: Single List Reordering
To create a sortable list, import the VueDraggable component and bind your reactive array using v-model:
<script setup lang="ts">
import { ref } from 'vue'
import { VueDraggable } from 'vue-draggable-plus'
interface Task {
id: number
title: string
}
const tasks = ref<Task[]>([
{ id: 1, title: 'Design System Audit' },
{ id: 2, title: 'Refactor Vue Router Guards' },
{ id: 3, title: 'Optimize Vite Build Bundle' },
{ id: 4, title: 'Write Pinia Unit Tests' },
])
</script>
<template>
<div class="max-w-md mx-auto p-4 bg-gray-50 rounded-lg shadow">
<h2 class="text-xl font-bold mb-4">Task Backlog</h2>
<VueDraggable
v-model="tasks"
:animation="300"
tag="ul"
class="flex flex-col gap-2"
>
<li
v-for="task in tasks"
:key="task.id"
class="p-3 bg-white rounded border border-gray-200 shadow-sm cursor-move flex items-center justify-between hover:border-blue-400"
>
<span>{{ task.title }}</span>
<span class="text-gray-400">⋮⋮</span>
</li>
</VueDraggable>
</div>
</template>Step 3: Moving Items Between Multiple Lists (Kanban Board)
To enable drag and drop between multiple lists, set the group prop to matching values across VueDraggable instances:
<script setup lang="ts">
import { ref } from 'vue'
import { VueDraggable } from 'vue-draggable-plus'
interface Card {
id: number
title: string
}
const todoColumn = ref<Card[]>([
{ id: 1, title: 'Setup Tailwind v4' },
{ id: 2, title: 'Configure Vitest' },
])
const doneColumn = ref<Card[]>([
{ id: 3, title: 'Upgrade to Vue 3.5' },
])
</script>
<template>
<div class="grid grid-cols-2 gap-4 max-w-4xl mx-auto p-4">
<!-- To Do Column -->
<div class="bg-gray-100 p-4 rounded-lg">
<h3 class="font-bold mb-3 text-gray-700">To Do</h3>
<VueDraggable
v-model="todoColumn"
group="kanban"
:animation="300"
class="flex flex-col gap-2 min-h-[150px]"
>
<div
v-for="card in todoColumn"
:key="card.id"
class="p-3 bg-white rounded shadow-sm cursor-move"
>
{{ card.title }}
</div>
</VueDraggable>
</div>
<!-- Done Column -->
<div class="bg-green-50 p-4 rounded-lg">
<h3 class="font-bold mb-3 text-green-800">Done</h3>
<VueDraggable
v-model="doneColumn"
group="kanban"
:animation="300"
class="flex flex-col gap-2 min-h-[150px]"
>
<div
v-for="card in doneColumn"
:key="card.id"
class="p-3 bg-white rounded shadow-sm cursor-move"
>
{{ card.title }}
</div>
</VueDraggable>
</div>
</div>
</template>Learn how to build a full Trello clone in Vue School's Build a Drag-and-Drop Trello Board with Vue.js course.
Approach 2: Native HTML5 Drag and Drop API (Best for File Drops)
When building a file upload drop zone or simple drag interactions, pulling in external dependencies is unnecessary. Vue 3 event directives integrate natively with the HTML5 Drag and Drop API.
Creating a Reactive File Drop Zone Component
<script setup lang="ts">
import { ref } from 'vue'
const isDragging = ref(false)
const uploadedFiles = ref<File[]>([])
const handleDragOver = () => {
isDragging.value = true
}
const handleDragLeave = () => {
isDragging.value = false
}
const handleDrop = (event: DragEvent) => {
isDragging.value = false
if (event.dataTransfer?.files) {
const files = Array.from(event.dataTransfer.files)
uploadedFiles.value.push(...files)
}
}
</script>
<template>
<div class="max-w-md mx-auto p-4">
<!-- Drop Target Area -->
<div
@dragover.prevent="handleDragOver"
@dragleave.prevent="handleDragLeave"
@drop.prevent="handleDrop"
:class="[
'p-8 border-2 border-dashed rounded-lg text-center transition-colors cursor-pointer',
isDragging ? 'border-blue-500 bg-blue-50' : 'border-gray-300 hover:border-gray-400'
]"
>
<p class="text-gray-600 font-medium">
{{ isDragging ? 'Drop files here...' : 'Drag and drop files here' }}
</p>
</div>
<!-- Uploaded Files List -->
<ul v-if="uploadedFiles.length > 0" class="mt-4 divide-y divide-gray-200">
<li v-for="(file, index) in uploadedFiles" :key="index" class="py-2 text-sm text-gray-700">
📄 {{ file.name }} ({{ (file.size / 1024).toFixed(1) }} KB)
</li>
</ul>
</div>
</template>Trade-offs of the Native HTML5 API
- Pros: Zero dependencies, browser native, high performance for file operations.
- Cons: Poor mobile touch support out of the box; complex list reordering logic requires manual array splicing; custom keyboard accessibility must be implemented manually.
Approach 3: Atlassian's pragmatic-drag-and-drop (Best for Enterprise & Accessibility)
Developed by Atlassian, pragmatic-drag-and-drop powers Jira and Trello. It is a headless, framework-agnostic drag-and-drop engine designed for high performance, complex nested drag structures, and accessibility compliance.
Installation
npm install @atlaskit/pragmatic-drag-and-dropVue 3 Integration Example
Connect pragmatic-drag-and-drop primitives using Vue 3 template refs and onMounted:
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { draggable, dropTarget } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'
const cardRef = ref<HTMLElement | null>(null)
const dropRef = ref<HTMLElement | null>(null)
const isDragged = ref(false)
const isOver = ref(false)
onMounted(() => {
if (cardRef.value) {
draggable({
element: cardRef.value,
onDragStart: () => { isDragged.value = true },
onDrop: () => { isDragged.value = false },
})
}
if (dropRef.value) {
dropTarget({
element: dropRef.value,
onDragEnter: () => { isOver.value = true },
onDragLeave: () => { isOver.value = false },
onDrop: () => { isOver.value = false },
})
}
})
</script>
<template>
<div class="flex gap-8 p-6 justify-center">
<!-- Draggable Element -->
<div
ref="cardRef"
:class="[
'p-4 bg-blue-600 text-white rounded-lg shadow cursor-grab select-none',
isDragged ? 'opacity-50' : 'opacity-100'
]"
>
Drag Me (Pragmatic)
</div>
<!-- Drop Target -->
<div
ref="dropRef"
:class="[
'p-8 border-2 border-dashed rounded-lg min-w-[200px] text-center',
isOver ? 'border-green-500 bg-green-50' : 'border-gray-300'
]"
>
Drop Zone
</div>
</div>
</template>Frequently Asked Questions (FAQ)
What is the best way to add drag and drop in Vue 3?
For most apps, use vue-draggable-plus. It wraps SortableJS, binds directly to your reactive data with v-model, and handles reordering, multi-list dragging, and mobile touch devices out of the box. Use the native HTML5 API for simple file upload zones, and pragmatic-drag-and-drop for enterprise applications requiring strict accessibility.
Should I use vuedraggable or vue-draggable-plus?
Use vue-draggable-plus for new Vue 3 projects. It is the actively maintained SortableJS wrapper designed specifically for Vue 3 Composition API (<script setup>) and TypeScript. Legacy vuedraggable (vuedraggable.next) still functions but receives fewer updates.
Can I implement drag and drop in Vue without a library?
Yes. The browser's native HTML5 Drag and Drop API works using the draggable="true" attribute combined with @dragstart, @dragover.prevent, and @drop event listeners. It is ideal for file drop targets, but cumbersome for sorting lists or supporting touch screens.
Does Vue drag and drop work on mobile and touch devices?
The native HTML5 API has weak touch support on mobile browsers. SortableJS-based libraries like vue-draggable-plus and Atlassian's pragmatic-drag-and-drop include native touch event fallbacks, making them far better suited for mobile interfaces.
How do I make Vue drag and drop accessible for keyboard and screen reader users?
Native drag and drop is difficult to render accessible without custom ARIA live regions and keyboard event listeners. Atlassian's pragmatic-drag-and-drop is designed with built-in accessibility primitives. When using vue-draggable-plus, supplement key elements with aria-grabbed and keyboard shortcut handlers (@keydown.space).
Conclusion
Congrats! You’ve nailed the basics of drag and drop in a Vue.js 3 application! Checkout more examples of how to use Vue Draggable on their official examples page. Plus, if you’re interested in taking your drag and drop skills to the next level, checkout our premium course: Build a Drag-and-Drop Trello Board with Vue.js.
In the course, we use Vue draggable to build a completely functional Trello style task board and dive into the library more in depth, along with other technologies like Tailwind CSS and VueUse ?. Here's a little preview of what's in store ?
Drag and drop is one interaction but if your looking to for deep dive into mastering vue.js join our flagship Vue.js 3 Master Class on Vue School.
For most apps, vue-draggable-plus. It wraps SortableJS, binds to your data with v-model, and handles reordering, moving between lists, and touch out of the box. Use the native HTML5 API only for simple cases like file drops, and pragmatic-drag-and-drop for complex, accessibility-heavy boards.
Use vue-draggable-plus for new Vue 3 projects. It is the actively maintained SortableJS wrapper, with Composition API and TypeScript support. vuedraggable still works but has seen less maintenance, so verify its current status before you build on it.
Yes. The browser's native HTML5 drag-and-drop API works with the draggable
attribute and the dragstart, dragover, and drop events. It is fine for a simple drop target, but it is awkward for sorting lists and needs extra work for touch and accessibility.
The native HTML5 API has weak touch support. SortableJS-based libraries like vue-draggable-plus, and pragmatic-drag-and-drop, handle touch far better, which is the main reason a library is worth it for anything past a basic drop zone.
Native drag and drop is hard to make keyboard and screen-reader friendly on its own. pragmatic-drag-and-drop is built with accessibility in mind, and SortableJS libraries give you a base you can extend with ARIA attributes and keyboard handlers. Test with a keyboard and a screen reader before you ship.
Start learning Vue.js for free

Comments
Latest Vue School Articles
5 Component Design Patterns to Boost Your Vue.js Applications

Vibe Coding a Collaborative Editor with Comment Support with Nuxt UI and Jazz

Our goal is to be the number one source of Vue.js knowledge for all skill levels. We offer the knowledge of our industry leaders through awesome video courses for a ridiculously low price.
More than 250.000 users have already joined us. You are welcome too!
© All rights reserved. Made with ❤️ by BitterBrains, Inc.


