# How to Implement Drag-and-Drop Functionality with the Element UI Transfer Component

> Learn to implement drag-and-drop with Element UI Transfer. Extend the component using draggable.js to sync items between panels and enhance user experience.

- Repository: [饿了么前端/element](https://github.com/ElemeFE/element)
- Tags: how-to-guide
- Published: 2026-03-07

---

**The Element UI Transfer component does not ship with built-in drag-and-drop capabilities, but you can extend it by leveraging the internal [`draggable.js`](https://github.com/ElemeFE/element/blob/main/draggable.js) helper and manually syncing the `value` array when items are dropped between panels.**

The **Transfer** component in the [ElemeFE/element](https://github.com/ElemeFE/element) repository provides a robust interface for moving items between two lists, yet it only supports button-based transfers by default. While the source code contains reusable drag utilities used by other components, implementing **drag-and-drop functionality with the Transfer component** requires connecting the [`draggable.js`](https://github.com/ElemeFE/element/blob/main/draggable.js) helper to the panel render logic. This guide demonstrates how to wire the internal drag system into [`main.vue`](https://github.com/ElemeFE/element/blob/main/main.vue) and [`transfer-panel.vue`](https://github.com/ElemeFE/element/blob/main/transfer-panel.vue) without breaking the existing API.

## Understanding the Transfer Component Architecture

Before adding drag support, you must understand how the component handles data flow. The Transfer component consists of two identical **TransferPanel** instances managed by a wrapper component.

The key files are:

- [`packages/transfer/src/main.vue`](https://github.com/ElemeFE/element/blob/main/packages/transfer/src/main.vue) – The root component that creates left and right panels, manages the `value` array, and exposes `addToLeft` and `addToRight` methods.
- [`packages/transfer/src/transfer-panel.vue`](https://github.com/ElemeFE/element/blob/main/packages/transfer/src/transfer-panel.vue) – The panel that renders checkboxes, handles filtering, and emits `checked-change` events.
- [`packages/color-picker/src/draggable.js`](https://github.com/ElemeFE/element/blob/main/packages/color-picker/src/draggable.js) – A framework-agnostic helper that attaches mouse event listeners to make any DOM element draggable.

## Step-by-Step Implementation Guide

### Import the Draggable Helper

The Element UI library includes a low-level drag utility originally built for the color picker. Import it into your custom wrapper or modified panel component:

```javascript
import draggable from 'element-ui/packages/color-picker/src/draggable';

```

### Attach Drag Handlers to Panel Items

Since [`transfer-panel.vue`](https://github.com/ElemeFE/element/blob/main/transfer-panel.vue) renders items as checkboxes inside `.el-transfer-panel__item` containers, you must initialize the draggable behavior after the DOM updates. In your custom panel extension or mounted hook, query these elements and bind the helper:

```javascript
mounted () {
  this.$nextTick(() => {
    this.$el.querySelectorAll('.el-transfer-panel__item').forEach(itemEl => {
      const key = itemEl.getAttribute('data-key');
      draggable(itemEl, {
        start: () => { this.draggedKey = key; },
        end: e => { this.handleDrop(e); }
      });
    });
  });
}

```

Ensure your template binds the key attribute: `:data-key="item[keyProp]"`.

### Detect Drop Targets and Emit Events

When a drag ends, determine whether the mouse released over the opposite panel. The `draggable` helper provides an `end` callback containing mouse coordinates. Emit a custom `drag-drop` event with the source panel, destination panel, and item key:

```javascript
handleDrop (event, targetPanel) {
  const target = targetPanel === this.$refs.leftPanel ? 'left' : 'right';
  const from = target === 'left' ? 'right' : 'left';
  this.$emit('drag-drop', { from, to: target, key: this.draggedKey });
  this.draggedKey = null;
}

```

### Update the Value Array in main.vue

In [`packages/transfer/src/main.vue`](https://github.com/ElemeFE/element/blob/main/packages/transfer/src/main.vue), listen for the `drag-drop` event on both panels and implement the logic to add or remove keys from the bound array:

```javascript
methods: {
  handleDragDrop ({ from, to, key }) {
    let newValue = this.value.slice();
    if (from === 'left' && to === 'right') {
      newValue.push(key);
    } else if (from === 'right' && to === 'left') {
      const idx = newValue.indexOf(key);
      if (idx > -1) newValue.splice(idx, 1);
    }
    this.$emit('input', newValue);
    this.$emit('change', newValue, to, [key]);
  }
}

```

This mirrors the behavior of the native `addToRight` and `addToLeft` methods while preserving reactivity.

## Complete Working Example

The following wrapper component demonstrates a production-ready implementation that integrates drag interactions while preserving the component's original API:

```html
<template>
  <el-transfer
    v-model="value"
    :data="list"
    :props="{key: 'id', label: 'name'}"
    @drag-drop="onDragDrop">
    <template #left-footer>
      <span>Drag items here →</span>
    </template>
    <template #right-footer>
      <span>← Drop items here</span>
    </template>
  </el-transfer>
</template>

<script>
import Transfer from 'element-ui/packages/transfer';
import draggable from 'element-ui/packages/color-picker/src/draggable';

export default {
  components: { Transfer },
  data () {
    return {
      value: [],
      list: [
        { id: 1, name: 'Option 1' },
        { id: 2, name: 'Option 2' },
        { id: 3, name: 'Option 3' }
      ],
      draggedKey: null
    };
  },
  mounted () {
    this.$nextTick(this.enableDrag);
  },
  methods: {
    enableDrag () {
      const panels = [this.$refs.leftPanel, this.$refs.rightPanel];
      panels.forEach(panel => {
        if (!panel) return;
        panel.$el.querySelectorAll('.el-transfer-panel__item').forEach(el => {
          const key = el.getAttribute('data-key');
          draggable(el, {
            start: () => { this.draggedKey = key; },
            end: event => this.handleDrop(event, panel)
          });
        });
      });
    },
    handleDrop (event, targetPanel) {
      const target = targetPanel === this.$refs.leftPanel ? 'left' : 'right';
      const from = target === 'left' ? 'right' : 'left';
      this.onDragDrop({ from, to: target, key: this.draggedKey });
      this.draggedKey = null;
    },
    onDragDrop ({ from, to, key }) {
      const newVal = this.value.slice();
      if (to === 'right') newVal.push(key);
      else {
        const idx = newVal.indexOf(key);
        if (idx > -1) newVal.splice(idx, 1);
      }
      this.value = newVal;
    }
  }
};
</script>

```

## Summary

- The **Transfer** component relies on [`packages/transfer/src/main.vue`](https://github.com/ElemeFE/element/blob/main/packages/transfer/src/main.vue) and [`packages/transfer/src/transfer-panel.vue`](https://github.com/ElemeFE/element/blob/main/packages/transfer/src/transfer-panel.vue) to manage item movement via the `addToLeft` and `addToRight` methods.
- The internal [`packages/color-picker/src/draggable.js`](https://github.com/ElemeFE/element/blob/main/packages/color-picker/src/draggable.js) helper provides native mouse-based dragging without external dependencies.
- To implement **drag-and-drop functionality with the Transfer component**, bind the helper to each `.el-transfer-panel__item`, track the dragged key, and emit custom events to update the `value` array.
- Always synchronize the bound array using `this.$emit('input', newValue)` to maintain reactive state consistency with the original component design.

## Frequently Asked Questions

### Does Element UI Transfer support drag and drop out of the box?

No. As implemented in the ElemeFE/element repository, the Transfer component only supports moving items via the center action buttons. The component architecture separates concerns between the panel display and movement logic, requiring manual integration of the [`draggable.js`](https://github.com/ElemeFE/element/blob/main/draggable.js) helper or a third-party library to enable drag interactions.

### Which files should I modify to add drag and drop to the Transfer component?

You should extend or wrap [`packages/transfer/src/transfer-panel.vue`](https://github.com/ElemeFE/element/blob/main/packages/transfer/src/transfer-panel.vue) to attach drag listeners to the checkbox items, and modify or intercept events in [`packages/transfer/src/main.vue`](https://github.com/ElemeFE/element/blob/main/packages/transfer/src/main.vue) to handle the actual data transfer. The reusable logic lives in [`packages/color-picker/src/draggable.js`](https://github.com/ElemeFE/element/blob/main/packages/color-picker/src/draggable.js), which you can import without altering the library source if you create a wrapper component.

### Can I use a third-party drag-and-drop library instead of the internal draggable helper?

Yes. While the internal [`draggable.js`](https://github.com/ElemeFE/element/blob/main/draggable.js) utility provides a lightweight, dependency-free solution, you can substitute libraries like SortableJS or Vue.Draggable. Replace the `enableDrag` method in your wrapper component to initialize the third-party library on the `.el-transfer-panel__list` container, then map the library's move events to the same `handleDragDrop` logic that updates the `value` array.

### How do I preserve keyboard accessibility when implementing custom drag and drop?

Keep the default action buttons visible as a fallback for keyboard users, or provide explicit keyboard shortcuts that trigger the same `handleDragDrop` method. Since the native Transfer component uses standard checkboxes and buttons, maintaining these elements ensures WCAG compliance even when drag interactions are layered on top.