# How to Create Entirely New Custom Widget Types from Scratch in Dear ImGui

> Learn to create custom widget types from scratch in Dear ImGui. Master ItemSize, ItemAdd, ButtonBehavior, and ImDrawList to build unique UIs efficiently.

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

---

**Creating custom widgets in Dear ImGui requires three fundamental steps: reserving layout space and registering an ID with `ItemSize()` and `ItemAdd()`, handling interaction states via `ButtonBehavior()`, and drawing primitives through the window's `ImDrawList`.**

Dear ImGui (ocornut/imgui) implements all UI elements—from simple buttons to complex color pickers—using a consistent immediate-mode architecture rather than hard-coded widget classes. This design allows developers to create entirely new custom widget types from scratch by replicating the same internal pattern used by the built-in components. By manipulating the draw list and layout system directly, custom widgets inherit automatic clipping, navigation support, and theme integration.

## The Three-Step Widget Architecture

Every widget in [`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp) follows an identical lifecycle. Mastering these three phases allows you to construct any interactive element.

### Step 1: Layout Reservation and ID Generation

First, your widget must declare its screen space and obtain a unique identifier. As implemented in `ImGui::ButtonEx` at lines 86–104 of **imgui_widgets.cpp**, this involves:

1.  Calling `window->GetID(label)` to generate an `ImGuiID` from the current ID stack.
2.  Calculating the bounding box (`ImRect bb`) based on `window->DC.CursorPos` and desired dimensions.
3.  Invoking `ItemSize(bb)` to advance the cursor and reserve vertical space.
4.  Calling `ItemAdd(bb, id)` to register the item with ImGui's layout and clipping system.

If `ItemAdd` returns false (indicating the widget is clipped or the window is collapsed), you should early-out to save processing time.

### Step 2: Interaction Handling with ButtonBehavior

Once registered, the widget must respond to input. Rather than handling mouse coordinates manually, call `ButtonBehavior()` as seen at lines 5450–5460 in **imgui_widgets.cpp**:

```cpp
bool hovered, held;
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);

```

This helper automatically manages mouse hover detection, click states, keyboard/gamepad navigation, and overlapping window checks. It respects `ImGuiButtonFlags` such as `ImGuiButtonFlags_PressedOnClickRelease` or `ImGuiButtonFlags_AllowOverlap`, ensuring your custom widget behaves like native ImGui elements.

### Step 3: Rendering via ImDrawList

Finally, draw the visual representation using the window's draw list. Access it via `ImGui::GetWindowDrawList()` and issue primitive commands like `AddRectFilled()`, `AddCircle()`, or `AddText()`. The demo example `ShowExampleAppCustomRendering` at lines 10248–10266 in **imgui_demo.cpp** demonstrates how to push custom geometry inside a widget-like function while respecting the current transform and clipping region.

## Complete Example: Building a Custom Toggle Switch

The following implementation creates a fully functional toggle switch widget that follows ImGui's architectural conventions:

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

    ImGuiContext& g = *GImGui;
    const ImGuiID id = window->GetID(label);
    const ImVec2 size(40.0f, 20.0f);
    const ImVec2 pos = window->DC.CursorPos;
    const ImRect bb(pos, pos + size);

    ImGui::ItemSize(size);
    if (!ImGui::ItemAdd(bb, id))
        return false;

    bool hovered, held;
    bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
    
    if (pressed)
        *v = !*v;

    ImDrawList* draw = ImGui::GetWindowDrawList();
    ImU32 col_bg = ImGui::GetColorU32(*v ? ImGuiCol_ButtonActive : ImGuiCol_Button);
    ImU32 col_knob = ImGui::GetColorU32(ImGuiCol_FrameBg);
    
    const float radius = size.y * 0.5f - 2.0f;
    const ImVec2 knob_center = *v 
        ? ImVec2(pos.x + size.x - radius - 2.0f, pos.y + size.y * 0.5f)
        : ImVec2(pos.x + radius + 2.0f, pos.y + size.y * 0.5f);

    draw->AddRectFilled(bb.Min, bb.Max, col_bg, size.y * 0.5f);
    draw->AddCircleFilled(knob_center, radius, col_knob);

    if (label[0])
    {
        ImVec2 label_size = ImGui::CalcTextSize(label);
        ImVec2 label_pos = ImVec2(bb.Max.x + g.Style.ItemInnerSpacing.x,
                                 bb.Min.y + (size.y - label_size.y) * 0.5f);
        ImGui::RenderText(label_pos, label);
    }

    return pressed;
}

```

**Key implementation details:**
- **Early-out optimization**: Checks `window->SkipItems` to respect collapsed windows.
- **ID uniqueness**: Uses `window->GetID()` to prevent collisions when the widget appears in loops.
- **State visualization**: Leverages `hovered` and `held` (returned by reference from `ButtonBehavior`) to adjust colors for visual feedback if desired.

## Advanced Interaction: Drag-and-Drop Support

Custom widgets can leverage ImGui's advanced features like drag-and-drop targets and sources. This color button example accepts color payloads while maintaining the core three-step structure:

```cpp
bool MyColorButton(const char* id_str, ImU32* color)
{
    ImGuiWindow* window = ImGui::GetCurrentWindow();
    if (window->SkipItems)
        return false;

    const ImVec2 size(36.0f, 36.0f);
    const ImVec2 pos = window->DC.CursorPos;
    const ImRect bb(pos, pos + size);
    const ImGuiID id = window->GetID(id_str);
    
    ImGui::ItemSize(size);
    if (!ImGui::ItemAdd(bb, id))
        return false;

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

    if (held && ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
    {
        ImGui::SetDragDropPayload("COLOR", color, sizeof(ImU32));
        ImGui::Text("Color: 0x%08X", *color);
        ImGui::EndDragDropSource();
    }

    if (ImGui::BeginDragDropTarget())
    {
        if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("COLOR"))
            memcpy(color, payload->Data, sizeof(ImU32));
        ImGui::EndDragDropTarget();
    }

    ImDrawList* draw = ImGui::GetWindowDrawList();
    draw->AddRectFilled(bb.Min, bb.Max, *color, 4.0f);
    draw->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(hovered ? ImGuiCol_BorderShadow : ImGuiCol_Border), 4.0f);

    return held;
}

```

## Essential Source Files for Reference

When creating entirely new custom widget types from scratch, consult these files in the ocornut/imgui repository:

- **[`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp)**: Contains reference implementations of standard widgets. Examine `ButtonEx` (lines 86–104) for layout/ID patterns and `ButtonBehavior` (lines 5450–5460) for interaction logic.
- **[`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h)**: Declares internal helpers like `ItemAdd()`, `ItemSize()`, and navigation utilities required for advanced widgets.
- **[`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp)**: Review `ShowExampleAppCustomRendering` (lines 10248–10266) for examples of low-level `ImDrawList` usage within widget contexts.
- **[`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_draw.cpp)**: Implements drawing primitives (`AddRectFilled`, `AddCircle`, etc.) used in the rendering phase.
- **[`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h)**: Provides the public API and style color enums (`ImGuiCol_Button`, `ImGuiCol_FrameBg`) for consistent theming.

## Summary

- **Layout and Identity**: Always call `ItemSize()` and `ItemAdd()` to register your widget's bounding box and ID, as demonstrated in [`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp).
- **Interaction**: Use `ButtonBehavior()` to handle mouse, keyboard, and gamepad input automatically without manual coordinate checks.
- **Rendering**: Draw via `ImDrawList` primitives obtained from `GetWindowDrawList()`, ensuring automatic clipping and transform application.
- **Integration**: Custom widgets respect `PushID()`/`PopID()` scopes, style colors, and window clipping when following the standard three-step pattern.

## Frequently Asked Questions

### How do I handle keyboard navigation in a custom ImGui widget?

Keyboard and gamepad navigation are automatically handled when you use `ButtonBehavior()` or similar interaction helpers from [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h). If you need custom navigation logic, check the return value of `ItemAdd()` for navigation highlight requests and call `SetItemDefaultFocus()` when appropriate.

### Can I create a custom widget that spans multiple frames or has internal state?

Yes, though ImGui is immediate-mode, you can persist state using `GetStateStorage()` or by allocating custom data via `GetID()` keys. For multi-frame animations or transitions, store state in your application code and pass it as parameters to your widget function each frame.

### Why does my custom widget not clip when the window is scrolled?

Ensure you call `ItemAdd()` before rendering and verify that your drawing commands use `GetWindowDrawList()` rather than `GetForegroundDrawList()` (unless you specifically need overlay rendering). The `ItemAdd()` function registers your bounding box with the current window's clip rect; anything drawn outside `bb` will be culled automatically.

### What is the difference between `ButtonBehavior` and `InvisibleButton`?

`InvisibleButton` found in [`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp) is a convenience wrapper that creates a hit-region without rendering. `ButtonBehavior` is the lower-level internal function that handles all input logic. Use `InvisibleButton` for simple hit-tests, but call `ButtonBehavior` directly when building custom widgets to gain fine-grained control over hover states, button flags, and return values.