Skip to content

Custom Draggable Layers

This example demonstrates how to customize the appearance of draggable elements by providing custom layer components, allowing for rich visual representations during drag operations.

Overview

The Custom Draggable Layers example shows how to replace the default appearance of dragged elements with completely custom Vue components. The library natively supports layers without requiring a custom overlay container - simply pass your layer component to the useDraggable composable.

⋮⋮
im default
⋮⋮
im with custom layer

Key Concepts

  • Creating custom layer components for draggable elements
  • Using the layer prop to specify custom representations
  • Native layer support without needing a custom container
  • Using markRaw to optimize performance when passing component references

Example Code

Draggable Component with Layer Support

vue
<script setup lang="ts">
  import { useDraggable, useSelection } from '@vue-dnd-kit/core';
  import { type Component } from 'vue';

  const { layer } = defineProps<{
    layer?: Component;
  }>();

  // The layer will be used automatically by the default container
  const { elementRef, handleDragStart } = useDraggable({
    layer,
  });

  const { handleToggleSelect, isSelected } = useSelection(elementRef);
</script>

<template>
  <div
    ref="elementRef"
    class="draggable"
  >
    <div class="draggable-actions">
      <div
        class="drag-handle"
        role="button"
        tabindex="0"
        aria-label="Drag handle"
        @pointerdown="handleDragStart"
      >
        ⋮⋮
      </div>
      <input
        :checked="isSelected"
        type="checkbox"
        @change="handleToggleSelect"
      />
    </div>
    <slot />
  </div>
</template>

Custom Layer Component

vue
<script setup lang="ts">
  import { TLayerProps } from '@vue-dnd-kit/core';

  const props = defineProps<TLayerProps>();
</script>

<template>
  <div>
    <img
      src="https://raw.githubusercontent.com/ZiZiGY/vue-dnd-kit/master/public/logo.svg"
      alt="logo"
    />
    {{ props.id }}
  </div>
</template>

Example Usage

vue
<script setup lang="ts">
  import { markRaw } from 'vue';
  import Draggable from './Draggable.vue';
  import Layer from './Layer.vue';
</script>

<template>
  <div class="example-container">
    <Draggable> im default </Draggable>
    <Draggable :layer="markRaw(Layer)"> im with custom layer </Draggable>
  </div>
</template>

<style scoped>
  .example-container {
    display: flex;
    gap: 12px;
    flex-direction: column;
  }
</style>

How It Works

Vue DnD Kit provides native support for custom layers:

  1. Built-in Support: The library natively renders your custom layers without requiring a custom overlay container
  2. Component Props: Your layer component receives useful props like id through the TLayerProps interface
  3. Default Fallback: Without a custom layer, the dragged element uses its original HTML
  4. Multiple Items: Each draggable can have its own custom layer for different visual representations

Using markRaw for Component References

When passing component references to the layer prop, always use markRaw() to prevent Vue from making the component reactive:

js
// Correct - use markRaw
import { markRaw } from 'vue';
import CustomLayer from './CustomLayer.vue';

<Draggable :layer="markRaw(CustomLayer)">
  Default content
</Draggable>

// Incorrect - will cause warnings and performance issues
<Draggable :layer="CustomLayer">
  Default content
</Draggable>

Working with Custom Containers

If you decide to use a custom overlay container, you'll need to handle layers yourself. Here's a simple example of how you might implement this:

vue
<!-- CustomOverlayContainer.vue -->
<script setup>
  import { useDragContainer } from '@vue-dnd-kit/core';

  const { elementRef, pointerPosition, draggingElements } = useDragContainer();

  // Your positioning logic here
  const computedStyle = computed(() => ({
    // Transform, position, etc.
  }));
</script>

<template>
  <div
    ref="elementRef"
    :style="computedStyle"
  >
    <template v-for="[_, element] in draggingElements">
      <!-- Render the custom layer if available -->
      <component
        v-if="element.layer"
        :is="element.layer"
        :id="element.id"
      />
      <!-- Fallback to the original HTML -->
      <div
        v-else
        v-html="element.initialHTML"
      ></div>
    </template>
  </div>
</template>

This technique gives you complete control over how your dragged elements appear, allowing for rich, contextual visual feedback during drag operations.

Released under the MIT License.