Skip to content

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.

Drag and drop inside an open Shadow DOM

The source, target, event routing, and scroll-parent lookup all operate inside this isolated shadow root.

View example code

Template

vue
<div
  ref="host"
  class="shadow-dom-demo__host"
  aria-label="Shadow DOM drag-and-drop example"
/>

TypeScript

ts
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;
});