FAQ
Does it support touch devices?
Yes. Drag gestures support mouse and touch input. The optional vibration prop on Drag can provide feedback on devices that implement navigator.vibrate().
Does it support SSR?
Yes. Components can be rendered on the server; browser event listeners and DOM access are initialized after mounting on the client. Import the library stylesheet through your application or framework CSS configuration.
Does it support keyboard dragging?
No. Version 3 supports pressing Escape to cancel an active mouse or touch drag, but it does not provide keyboard-driven pickup, movement, or dropping. Applications that require keyboard reordering should provide equivalent controls and appropriate accessibility semantics.
Can this be used with Nuxt?
Yes. The components are SSR-compatible. Add vue-easy-dnd/style.css to your Nuxt CSS configuration or import it once from client application code.
Does it work inside Shadow DOM?
Yes. Open shadow roots are supported for mouse and touch target detection, composed drag movement, Drop event routing, and scroll-parent discovery across the shadow host. Closed shadow roots are not supported.
The source, target, event routing, and scroll-parent lookup all operate inside this isolated shadow root.
View example code
Template
<div
ref="host"
class="shadow-dom-demo__host"
aria-label="Shadow DOM drag-and-drop example"
/>TypeScript
import { createApp, defineComponent, h, onBeforeUnmount, onMounted, ref } from 'vue';
import { Drag, Drop } from 'vue-easy-dnd';
import type { DnDEventPayload } from 'vue-easy-dnd';
const shadowStyles = `
:host { display: block; height: 20rem; overflow: auto; border: 2px solid #7c3aed; border-radius: 10px; }
* { box-sizing: border-box; }
.content { min-height: 38rem; padding: 1rem; color: #1f2937; background: #faf5ff; font: 15px/1.5 system-ui, sans-serif; }
.source { display: inline-block; padding: .7rem 1rem; border-radius: 8px; background: #7c3aed; color: white; cursor: grab; user-select: none; }
.target { display: grid; min-height: 7rem; margin-top: 22rem; place-content: center; border: 2px dashed #7c3aed; border-radius: 9px; text-align: center; }
.target.drop-in { background: #ede9fe; }
`;
const ShadowContent = defineComponent({
name: 'ShadowDndContent',
setup () {
const result = ref('Drag the purple card, scroll down, and drop it here.');
const onDrop = (event: DnDEventPayload) => {
result.value = `Dropped: ${String(event.data)}`;
};
return () => h('div', { class: 'content' }, [
h('style', shadowStyles),
h(Drag, { type: 'shadow-card', data: 'Shadow DOM card' }, {
default: () => h('div', { class: 'source' }, 'Drag inside the shadow root')
}),
h(Drop, { acceptsType: 'shadow-card', class: 'target', onDrop }, {
default: () => result.value
})
]);
}
});
const host = ref<HTMLElement | null>(null);
let shadowApp: ReturnType<typeof createApp> | null = null;
onMounted(() => {
if (!host.value) return;
const shadowRoot = host.value.attachShadow({ mode: 'open' });
const mountPoint = document.createElement('div');
shadowRoot.appendChild(mountPoint);
shadowApp = createApp(ShadowContent);
shadowApp.mount(mountPoint);
});
onBeforeUnmount(() => {
shadowApp?.unmount();
shadowApp = null;
});