# How to Implement Drag and Drop Functionality Between ImGui Windows

> Learn how to implement drag and drop functionality between ImGui windows using its global subsystem. Explore payload state and accept drops across all windows with this easy guide.

- Repository: [omar/imgui](https://github.com/ocornut/imgui)
- Tags: how-to-guide
- Published: 2026-07-16

---

**TLDR:** Dear ImGui provides a global drag-and-drop subsystem that works across all windows by storing payload state in the global `ImGuiContext`, allowing any window to accept drops from any other window within the same context.

Implementing drag and drop functionality between ImGui windows relies on Dear ImGui's global state management rather than window-specific handling. According to the `ocornut/imgui` source code, the drag-and-drop system stores active payloads in the global `ImGuiContext` defined in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp), making cross-window transfers seamless as long as both windows belong to the same context. This architecture means you use the same API calls whether dragging within a single window or between separate windows.

## Understanding the Global Drag-and-Drop State

The drag-and-drop state (`g.DragDropActive`, `g.DragDropPayload`) lives in the global `ImGuiContext`. When a source in Window A activates a drag via `BeginDragDropSource()`, the context stores the source ID and payload data. Any subsequent call to `BeginDragDropTarget()` in Window B consults this same global state, allowing the payload to be accepted regardless of which window created it. The only requirement is that the target's hover test succeeds and the target ID differs from the source ID.

## Setting Up the Drag Source

To initiate a drag operation, call `ImGui::BeginDragDropSource()` on the source widget. In [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp), this function validates the item ID and activates the global drag state.

If the widget lacks a unique identifier (such as `Text()` or `Image()`), you must pass the `ImGuiDragDropFlags_SourceAllowNullID` flag:

```cpp
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) {
    // Drag logic here
    ImGui::EndDragDropSource();
}

```

### Configuring the Payload Data

While the source is active, call `ImGui::SetDragDropPayload()` exactly once to supply the data. This function, implemented in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp), copies the payload into internal buffers and records the type string (maximum 32 characters):

```cpp
const char* payload_data = "Cross-window data";
ImGui::SetDragDropPayload("MY_TYPE", payload_data, strlen(payload_data) + 1);

```

Call `ImGui::EndDragDropSource()` to finalize the operation. This hides the preview tooltip and discards the payload if it was never accepted.

## Creating Cross-Window Drop Targets

In the destination window, call `ImGui::BeginDragDropTarget()` to declare a drop zone. According to the source code in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp), this verifies that the mouse is hovering a valid item (the `ImGuiItemStatusFlags_HoveredRect` flag) and that the target ID differs from the source ID. If the target item has no ID, ImGui creates a temporary one from the item's rectangle.

### Accepting and Validating Payloads

Use `ImGui::AcceptDragDropPayload()` to check for compatible payload types:

```cpp
if (ImGui::BeginDragDropTarget()) {
    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_TYPE")) {
        const char* data = (const char*)payload->Data;
        // Process data here
    }
    ImGui::EndDragDropTarget();
}

```

Call `ImGui::EndDragDropTarget()` to clear the target state and discard the delivered payload immediately.

### Controlling Visual Feedback

ImGui automatically draws a default drop rectangle when hovering valid targets. To disable this, use the `ImGuiDragDropFlags_AcceptNoDrawDefaultRect` flag. You can also suppress the source preview tooltip using `ImGuiDragDropFlags_SourceNoPreviewTooltip` or `ImGuiDragDropFlags_AcceptNoPreviewTooltip` when specific visual customization is required.

## Complete Cross-Window Implementation Example

The following example demonstrates drag and drop between two separate ImGui windows, as referenced in [`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp):

```cpp
// Window A - Drag Source
if (ImGui::Begin("Source Window")) {
    ImGui::Text("Drag the button below:");
    if (ImGui::Button("Drag Me")) {
        // Button activation handles the item ID
    }
    if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
        const char* payload = "Hello from Source";
        ImGui::SetDragDropPayload("MY_STRING", payload, strlen(payload) + 1);
        ImGui::TextColored(ImVec4(1,1,0,1), "Dragging...");
        ImGui::EndDragDropSource();
    }
    ImGui::End();
}

// Window B - Drop Target
if (ImGui::Begin("Target Window")) {
    ImGui::Text("Drop here:");
    if (ImGui::BeginDragDropTarget()) {
        if (const ImGuiPayload* pl = ImGui::AcceptDragDropPayload("MY_STRING")) {
            const char* received = (const char*)pl->Data;
            ImGui::Text("Received: %s", received);
        }
        ImGui::EndDragDropTarget();
    }
    ImGui::End();
}

```

This works across windows because both access the same global `ImGuiContext` drag state, allowing seamless transfer of the `"MY_STRING"` payload type.

## Summary

- **Global State**: Drag-and-drop data lives in `ImGuiContext` (`g.DragDropActive`, `g.DragDropPayload`), enabling cross-window transfers between any windows sharing the same context.
- **Source Setup**: Use `BeginDragDropSource()` and `SetDragDropPayload()` to initiate drags; include `ImGuiDragDropFlags_SourceAllowNullID` for widgets without unique IDs.
- **Target Setup**: Use `BeginDragDropTarget()` and `AcceptDragDropPayload()` to receive data; the function validates payload types against the 32-character type identifier.
- **Cleanup**: Always call `EndDragDropSource()` and `EndDragDropTarget()` to properly manage payload lifecycle and visual feedback.
- **Core Files**: Implementation details are found in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) (core logic) and [`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp) (reference examples), with declarations in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h).

## Frequently Asked Questions

### Can I drag and drop between ImGui windows that are in different contexts?

No. The drag-and-drop system relies on global state stored in `ImGuiContext`. If windows belong to different contexts (rare in standard applications), they cannot share drag payloads. Ensure both windows use the same context by calling `ImGui::SetCurrentContext()` with the same pointer, or stick to the default global context.

### What happens if the source widget doesn't have a unique ID?

Widgets like `ImGui::Text()` or `ImGui::Image()` lack automatic IDs. According to the source in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp), you must pass `ImGuiDragDropFlags_SourceAllowNullID` to `BeginDragDropSource()`. Without this flag, the function will assert or fail because it cannot track the drag source properly.

### How long does the payload data remain valid?

The payload remains valid from the call to `SetDragDropPayload()` until `EndDragDropSource()` completes (if rejected) or `EndDragDropTarget()` executes (if accepted). The data is copied into ImGui's internal buffers during `SetDragDropPayload()`, so you don't need to maintain the original variable throughout the drag operation.

### Can I customize the visual feedback during drag-and-drop operations?

Yes. Use `ImGuiDragDropFlags_SourceNoPreviewTooltip` to suppress the default tooltip preview, or render custom content between `BeginDragDropSource()` and `EndDragDropSource()`. For drop targets, use `ImGuiDragDropFlags_AcceptNoDrawDefaultRect` to prevent the default rectangle, then draw your own highlight using `ImGui::GetItemRectMin()` and `ImGui::GetItemRectMax()`.