# How to Create Custom ImGui Widgets: A Complete Guide to Extending Dear ImGui

> Learn how to create custom ImGui widgets with this complete guide. Understand ID generation, item registration, interaction handling, and rendering for ImGui development.

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

---

**Creating custom ImGui widgets requires generating a unique ID with `GetID()`, defining a bounding rectangle, registering the item with `ItemAdd()`, handling interaction states via `ButtonBehavior()`, and rendering visuals using the `ImDrawList` API.**

The Dear ImGui library (ocornut/imgui) provides a modular architecture that allows developers to implement interactive UI elements following the same patterns used by built-in controls. By understanding the internal item system and interaction helpers, you can create custom widgets that fully integrate with ImGui's navigation, focus management, and styling systems.

## The Four-Step Pattern for Custom ImGui Widgets

Every widget in Dear ImGui follows a consistent implementation pattern visible throughout [`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp). This blueprint ensures your custom controls behave identically to native buttons, sliders, and checkboxes.

### 1. Define an ID and Bounding Rectangle

Every widget requires a unique **ImGuiID** to identify it within the window's item stack. Call `window->GetID(label)` to generate a stable identifier based on the current ID stack and label string. Simultaneously, calculate an **ImRect** describing the widget's screen area using `window->DC.CursorPos` and `ImGui::CalcItemSize()`.

### 2. Register with ItemAdd

Call `ImGui::ItemAdd(bb, id)` to register the widget with ImGui's item system. This function, declared in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h), makes ImGui aware of the item so it participates in focus, hover, and navigation handling. If `ItemAdd` returns false, the widget is clipped or otherwise inactive, and you should return early. For navigation data, use the overload `ItemAdd(bb, id, &nav_legacy)`.

### 3. Handle Interaction with ButtonBehavior

The `ImGui::ButtonBehavior()` function, implemented in [`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp) at line 461, encapsulates mouse, keyboard, and gamepad interaction logic. Pass the bounding box, ID, and output parameters for `hovered` and `held` states. The function returns true when the widget is pressed. Customize behavior using flags like `ImGuiButtonFlags_Repeat` for held-button repeating actions or `ImGuiButtonFlags_PressedOnDragDropHold` for drag-and-drop support.

### 4. Render with ImDrawList

After processing interaction, render the widget using `window->DrawList->Add...` functions. The **ImDrawList** API provides methods for drawing rectangles, text, lines, and complex shapes. Use `ImGui::RenderTextClipped()` for text alignment within the bounding box and `ImGui::GetColorU32()` to respect the current style colors.

## Complete Custom Widget Implementation

Here is the canonical implementation pattern used throughout the Dear ImGui source code:

```cpp
bool MyCustomWidget(const char* label, MyData* data)
{
    ImGuiWindow* window = ImGui::GetCurrentWindow();
    if (window->SkipItems) return false;

    // 1. ID & bounding box
    ImGuiID id = window->GetID(label);
    ImVec2 pos = window->DC.CursorPos;
    const ImVec2 size = ImGui::CalcItemSize(ImVec2(0,0), 100, ImGui::GetFrameHeight());
    const ImRect bb(pos, pos + size);

    // 2. Register the item
    if (!ImGui::ItemAdd(bb, id))
        return false;

    // 3. Interaction logic (hover/press)
    bool hovered, held;
    bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held,
                     ImGuiButtonFlags_None);

    // 4. Rendering (custom look)
    ImU32 col_bg = ImGui::GetColorU32(held ? ImGuiCol_ButtonActive :
                                      hovered ? ImGuiCol_ButtonHovered :
                                      ImGuiCol_Button);
    window->DrawList->AddRectFilled(bb.Min, bb.Max, col_bg, ImGui::GetStyle().FrameRounding);
    ImGui::RenderTextClipped(bb.Min, bb.Max, label, nullptr, nullptr,
                             ImVec2(0.5f, 0.5f), nullptr);

    return pressed;
}

```

## Practical Custom Widget Examples

These examples demonstrate specific interaction patterns found in [`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp) and [`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp).

### Simple Toggle Button

This example combines a standard button with custom coloring to display binary state:

```cpp
bool MyToggle(const char* label, bool* v)
{
    if (ImGui::Button(label)) *v = !*v;
    ImGui::SameLine();
    ImGui::TextColored(*v ? ImVec4(0,1,0,1) : ImVec4(1,0,0,1), *v ? "ON" : "OFF");
    return *v;
}

```

### Custom Color Picker with Draggable Hue Bar

This implementation uses `ButtonBehavior` to create a draggable hue selector, referencing the pattern used in ImGui's color picker internals:

```cpp
bool MyColorPicker(const char* label, ImVec4* col)
{
    ImGuiWindow* win = ImGui::GetCurrentWindow();
    ImGuiID id = win->GetID(label);
    ImVec2 p = win->DC.CursorPos;
    const float hue_bar_width = 200.0f;
    const ImRect bb(p, p + ImVec2(hue_bar_width, ImGui::GetFrameHeight()));
    if (!ImGui::ItemAdd(bb, id)) return false;

    bool hovered, held;
    ImGui::ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
    if (held)
    {
        float t = (ImGui::GetIO().MousePos.x - bb.Min.x) / hue_bar_width;
        t = ImClamp(t, 0.0f, 1.0f);
        ImGui::ColorConvertHSVtoRGB(t, 1.0f, 1.0f, col->x, col->y, col->z);
    }

    // draw hue gradient
    win->DrawList->AddRectFilledMultiColor(bb.Min, bb.Max,
        ImGui::GetColorU32(ImVec4(1,0,0,1)),
        ImGui::GetColorU32(ImVec4(1,1,0,1)),
        ImGui::GetColorU32(ImVec4(0,1,0,1)),
        ImGui::GetColorU32(ImVec4(0,1,1,1)));
    float marker_x = bb.Min.x + hue_bar_width * ImGui::ColorConvertRGBtoHSV(col->x, col->y, col->z, nullptr, nullptr, nullptr);
    win->DrawList->AddLine(ImVec2(marker_x, bb.Min.y), ImVec2(marker_x, bb.Max.y),
        ImGui::GetColorU32(ImGuiCol_Text), 2.0f);
    return held;
}

```

### Drag-and-Drop Target Widget

This example demonstrates integrating with ImGui's drag-and-drop system using `ButtonBehavior` for hover detection:

```cpp
bool MyDragDropTarget(const char* label, void* payload)
{
    ImGuiWindow* win = ImGui::GetCurrentWindow();
    ImGuiID id = win->GetID(label);
    ImVec2 p = win->DC.CursorPos;
    const ImRect bb(p, p + ImGui::CalcItemSize(ImVec2(0,0), 100, 0));
    if (!ImGui::ItemAdd(bb, id)) return false;

    bool hovered, held;
    ImGui::ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);

    if (ImGui::BeginDragDropTarget()) {
        if (const ImGuiPayload* pl = ImGui::AcceptDragDropPayload("MY_TYPE")) {
            memcpy(payload, pl->Data, pl->DataSize);
            ImGui::EndDragDropTarget();
            return true;
        }
        ImGui::EndDragDropTarget();
    }

    win->DrawList->AddRect(bb.Min, bb.Max,
        ImGui::GetColorU32(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button));
    ImGui::RenderTextClipped(bb.Min, bb.Max, label, nullptr, nullptr,
        ImVec2(0.5f, 0.5f), nullptr);
    return false;
}

```

## Advanced Interaction Techniques

When building complex custom widgets in Dear ImGui, consider these specialized patterns found in the source code:

- **Keyboard and Gamepad Navigation**: Pass `ImGuiButtonFlags_NoNavFocus` or `ImGuiButtonFlags_NoNav` to `ButtonBehavior()` to control navigation focus, or set `ImGuiItemFlags_NoNav` via `PushItemFlag()` before `ItemAdd()`.

- **Drag-and-Drop Support**: Use `ImGuiButtonFlags_PressedOnDragDropHold` with `ButtonBehavior()` and check `g.DragDropActive` to handle drag sources and targets properly.

- **Repeat Actions**: For buttons that should trigger repeatedly while held, pass `ImGuiButtonFlags_Repeat` to `ButtonBehavior()` or add the `ImGuiItemFlags_ButtonRepeat` flag to the item.

- **Manual ID Management**: If you bypass `ItemAdd()`, you must call `KeepAliveID(id)` manually to prevent the ID from being recycled, as noted in the comment at line 5658 of [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp).

- **ID Conflict Resolution**: If your widget uses multiple interactive areas with the same ID base, temporarily disable duplicate-ID checks using `PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true)` as referenced in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) (lines 631-645).

## Essential Source Files for Custom Widget Development

Understanding these key files in the ocornut/imgui repository provides the necessary context for advanced widget development:

- **[`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h)**: Contains the public API including `Begin()`, `End()`, and `GetID()` that form the foundation of widget interaction.

- **[`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h)**: Declares internal helpers including `ItemAdd()`, `ButtonBehavior()` (line 3766), and navigation flags required for custom implementations.

- **[`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp)**: Houses the `ButtonBehavior` implementation (line 461) and serves as the reference for built-in widgets like `Button()` and `Checkbox()`.

- **[`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp)**: Contains core engine logic including the `KeepAliveID()` comment (line 5658) relevant when bypassing `ItemAdd()`, and ID conflict handling (lines 631-645).

- **[`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp)**: Demonstrates practical widget combinations and higher-level UI patterns.

## Summary

- **ID Generation**: Use `window->GetID()` to create stable identifiers that respect ImGui's ID stack.
- **Item Registration**: Always call `ItemAdd()` with your bounding box and ID to enable focus, hover, and navigation handling.
- **Interaction Handling**: Leverage `ButtonBehavior()` from [`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp) to process mouse, keyboard, and gamepad input consistently.
- **Rendering**: Draw custom visuals using `window->DrawList` after interaction processing to ensure responsive feedback.
- **Advanced Features**: Control navigation, drag-and-drop, and repeat behavior through specific flags passed to `ButtonBehavior()` or `PushItemFlag()`.

## Frequently Asked Questions

### What is the minimum code needed for a custom ImGui widget?

The absolute minimum requires four elements: generate an ID with `GetID()`, define an `ImRect` bounding box, register with `ItemAdd(bb, id)`, and return a boolean indicating activation. While you can skip `ButtonBehavior()` for non-interactive display items, any clickable widget should use it to handle hover and pressed states consistently with the rest of the library.

### How do I handle keyboard navigation in custom widgets?

Pass appropriate flags to `ButtonBehavior()` such as `ImGuiButtonFlags_NoNavFocus` to prevent focus capture, or use `PushItemFlag(ImGuiItemFlags_NoNav)` before `ItemAdd()` to exclude the widget from navigation entirely. For full navigation support, ensure your widget responds to the `ImGuiKey_Enter` or `ImGuiKey_Space` inputs that `ButtonBehavior()` processes automatically when the item is focused.

### Can I create custom widgets without including imgui_internal.h?

While basic drawing is possible using only [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h), creating interactive widgets that properly handle focus, navigation, and input requires [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h). This header provides access to `ItemAdd()`, `ButtonBehavior()`, and the `ImGuiWindow` structure needed to access `window->DrawList` and `window->DC.CursorPos`.

### How do I avoid ID conflicts when creating multiple instances of my widget?

Dear ImGui automatically handles ID uniqueness through its ID stack mechanism when you use `GetID(label)`. However, if your widget contains multiple interactive sub-elements (like a complex slider with separate drag areas), use `PushID()` before generating IDs for sub-components, or temporarily allow duplicate IDs with `PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true)` as shown in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) lines 631-645.