# How to Implement Drag and Drop Functionality in Dear ImGui: A Complete API Guide

> Learn to implement drag and drop functionality in Dear ImGui. Master BeginDragDropSource, SetDragDropPayload, and BeginDragDropTarget with this complete guide for seamless UI interactions.

- Repository: [omar/imgui](https://github.com/ocornut/imgui)
- Tags: api-reference
- Published: 2026-07-30

---

**Dear ImGui provides an immediate-mode drag-and-drop system where `BeginDragDropSource()` initiates drags from any widget, `SetDragDropPayload()` packages the data, and `BeginDragDropTarget()` with `AcceptDragDropPayload()` handles the drop on compatible targets.**

Dear ImGui's drag-and-drop implementation operates entirely within the immediate-mode paradigm, requiring no persistent state management or complex event registration. According to the ocornut/imgui source code, the system relies on a simple three-phase workflow executed frame-by-frame: detecting drag initiation on source widgets, transporting payload data via the `ImGuiPayload` structure, and accepting drops on target widgets. This architecture allows developers to implement complex interactions like item reordering, file imports, or property transfers with minimal boilerplate.

## Understanding the Drag and Drop Workflow

The API divides drag-and-drop operations into distinct source and target phases, each bracketed by begin/end functions declared in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h).

### Source Phase: Initiating the Drag

To make a widget draggable, call `BeginDragDropSource()` immediately after submitting the widget. This function returns `true` once the user holds the mouse button and begins dragging.

- **`BeginDragDropSource([flags])`** – Defined in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) at lines 73‑79. Returns `true` when the widget becomes active drag source.
- **`SetDragDropPayload(type, data, size, cond)`** – Defined in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) at lines 80‑81. Copies the payload data (max 32 characters for the type string) into internal storage, allowing the original buffer to be freed immediately after the call.
- **`EndDragDropSource()`** – Defined in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) at line 82. Must be called only when `BeginDragDropSource()` returned `true`.

### Target Phase: Receiving the Drop

To accept drops, submit your target widget (using any standard item call like `Button()` or `InvisibleButton()`), then call `BeginDragDropTarget()`.

- **`BeginDragDropTarget()`** – Defined in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) at lines 84‑85. Returns true while a compatible payload hovers over the item's bounding box.
- **`AcceptDragDropPayload(type, [flags])`** – Defined in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) at lines 86‑87. Returns a pointer to `ImGuiPayload` (defined in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h)) if the hovering payload matches the specified type string; otherwise returns `NULL`.
- **`EndDragDropTarget()`** – Defined in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) at line 88. Must be called only when `BeginDragDropTarget()` returned `true`.

### The Payload Structure

The `ImGuiPayload` structure (defined in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h)) contains the `Data` pointer, `DataSize`, and `Type` string. According to the implementation in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) around line 15292, the system copies payload data when `SetDragDropPayload` is called, ensuring the payload remains valid even if the source widget is destroyed during the drag operation.

## Practical Implementation Examples

### Swapping Items Between Buttons

This pattern from [`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp) demonstrates a complete copy/move/swap interaction using button widgets as both sources and targets. Notice the use of `PushID()`/`PopID()` to ensure unique `ImGuiID`s for drag detection.

```cpp
static const char* names[3] = { "Alice", "Bob", "Carol" };
for (int n = 0; n < IM_ARRAYSIZE(names); n++)
{
    ImGui::PushID(n);
    ImGui::Button(names[n], ImVec2(80, 30));

    // ---- Drag source -------------------------------------------------
    if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
    {
        // Payload carries the index of the item.
        ImGui::SetDragDropPayload("MY_DND_PAYLOAD", &n, sizeof(int));
        ImGui::Text("Dragging %s", names[n]);   // optional preview tooltip
        ImGui::EndDragDropSource();
    }

    // ---- Drop target -------------------------------------------------
    if (ImGui::BeginDragDropTarget())
    {
        if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND_PAYLOAD"))
        {
            int src_idx = *(const int*)payload->Data;
            // Example: swap the two entries.
            ImGui::Swap(names[n], names[src_idx]);
        }
        ImGui::EndDragDropTarget();
    }
    ImGui::PopID();
}

```

### Dragging Colors Between Widgets

This minimal example shows dragging an `ImVec4` color from a color button to a custom rectangle target using `InvisibleButton()` as the drop zone.

```cpp
static ImVec4 source_color = ImVec4(0.4f, 0.7f, 0.2f, 1.0f);
static ImVec4 target_color = ImVec4(0.2f, 0.3f, 0.8f, 1.0f);

ImGui::ColorButton("Source", source_color, ImGuiColorEditFlags_NoLabel);
if (ImGui::BeginDragDropSource())
{
    ImGui::SetDragDropPayload("COL_DND", &source_color, sizeof(ImVec4));
    ImGui::Text("Drag Color");
    ImGui::EndDragDropSource();
}

ImGui::SameLine();
ImGui::InvisibleButton("Target", ImVec2(80, 80));
if (ImGui::BeginDragDropTarget())
{
    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("COL_DND"))
        target_color = *(const ImVec4*)payload->Data;
    ImGui::EndDragDropTarget();
}
ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetItemRectMin(),
                                          ImGui::GetItemRectMax(),
                                          ImGui::ColorConvertFloat4ToU32(target_color));

```

## Key Implementation Details

**Widget IDs are mandatory** – Drag-and-drop operations depend on `ImGuiID`. If you attempt to use `BeginDragDropSource()` after a widget without a natural ID (such as `ImGui::Text`), you must wrap the content with `PushID()`/`PopID()` pairs to generate a unique identifier.

**Available flags** – The `ImGuiDragDropFlags` enumeration (defined in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h)) provides several control options:
- **`ImGuiDragDropFlags_SourceNoPreviewTooltip`** – Disables the default preview tooltip on the source side.
- **`ImGuiDragDropFlags_AcceptNoPreviewTooltip`** – Hides the tooltip when hovering over a target.
- **`ImGuiDragDropFlags_AcceptBeforeDelivery`** – Allows the target to accept and preview the payload before the mouse button is released (peek mode).

**Nesting restrictions** – The implementation in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) (around line 15037) asserts that sources cannot be nested inside other sources or targets. Calling `BeginDragDropSource()` while another drag operation is active triggers `IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()")`.

**Payload lifetime** – Because `SetDragDropPayload()` copies the data immediately, the source buffer can be stack-allocated or reused once the function returns. The internal copy persists until the drag operation completes or the payload is accepted by a target.

## Summary

- **Dear ImGui's drag-and-drop system** is immediate-mode, utilizing `BeginDragDropSource()` and `BeginDragDropTarget()` bracketed calls defined in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h).
- **Payload data** is copied and transported via `SetDragDropPayload()`, with type strings limited to 32 characters.
- **Widget IDs** are essential for source detection; use `PushID()`/`PopID()` for text or custom draw widgets.
- **The `ImGuiPayload` structure** provides the data pointer and size, accessible via `AcceptDragDropPayload()` as implemented in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) around line 15292.
- **Source and target blocks** cannot be nested, and the API provides flags to control tooltip visibility and early acceptance behavior.

## Frequently Asked Questions

### What is the maximum size for drag-and-drop payload type strings?

Dear ImGui limits payload type strings to **32 characters** (including the null terminator). This is enforced internally in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) during the call to `SetDragDropPayload()`. Exceeding this limit results in the type being truncated, potentially causing type mismatches between source and target.

### Can I implement drag and drop on text labels or custom drawn elements?

Yes, but you must explicitly provide an `ImGuiID` using `PushID()` before the text or drawing command, and `PopID()` after. The drag-and-drop detection mechanism in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) relies on the last item ID returned by `GetID()`, so widgets without automatic ID generation (like `ImGui::Text` or raw draw lists) require manual ID management to serve as valid sources or targets.

### How do I preview the payload data before the user releases the mouse button?

Use the **`ImGuiDragDropFlags_AcceptBeforeDelivery`** flag when calling `AcceptDragDropPayload()`. This allows the target to access `payload->Data` during the hover state (while the mouse button is still held), enabling real-time visual feedback such as highlighting insertion points or displaying preview overlays before the drop is finalized.

### Why does my drag operation trigger an assertion error?

The most common cause is **nesting** drag-and-drop source or target blocks, which triggers the `IM_ASSERT(g.DragDropWithinSource)` check in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp). Ensure you never call `BeginDragDropSource()` inside another source or target block, and always verify the return value of begin functions before calling the corresponding `EndDragDropSource()` or `EndDragDropTarget()`.