# How to Implement Custom Widget Types by Extending Dear ImGui

> Learn to implement custom widget types in Dear ImGui by extending its core functionality. Generate unique IDs, handle interactions, register items, and render custom widgets efficiently.

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

---

**Dear ImGui is designed to be extensible without touching the core source files; you implement custom widget types by generating unique IDs with `GetID()`, handling interaction via `ButtonBehavior()`, registering the item with `ItemAdd()`, and rendering through the window's draw list.**

Dear ImGui (ocornut/imgui) provides an immediate-mode GUI architecture that allows developers to create custom widgets without modifying the library's internals. By following the same patterns found in [`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp), you can add specialized controls that integrate seamlessly with ImGui's navigation, styling, and input handling systems.

## The Anatomy of a Custom Widget

Creating a custom widget involves six distinct steps that mirror the internal implementation of built-in controls like `Button()` and `Checkbox()`.

### Generate a Unique ID

Every widget requires a unique identifier to handle interaction state, navigation, and the ID stack. Use `ImGui::GetID()` or the `PushID()`/`PopID()` helpers to generate this identifier. In [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) at approximately line 1512, `window->GetID(label)` hashes the string with the current ID stack seed to produce a unique `ImGuiID`.

### Define the Interaction Rectangle

Calculate the widget's bounding box (`ImRect`) based on content size and frame padding. Call `ImGui::ItemSize()` to advance the cursor and `ImGui::ItemAdd()` (defined in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) at line 3616) to register the item with the UI system. Registration enables automatic clipping, navigation, and tooltip support.

### Handle Interaction with ButtonBehavior

Call `ImGui::ButtonBehavior()` (located in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) at line 3766) to process mouse input, hover states, focus, and navigation. This helper encapsulates the complex state machine for button logic, including repeat and double-click handling.

The function signature is:

```cpp
bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0);

```

### Render the Visual Appearance

Use `ImGui::GetWindowDrawList()` to access low-level drawing functions like `AddRectFilled()`, `AddText()`, and `RenderFrame()`. These ensure your widget respects the current color theme and style settings.

### Return Interaction Results

Return a boolean (or custom value) indicating whether the widget was activated, following the convention of built-in functions.

## Complete Implementation: Custom Toggle Button

Below is a complete, compilable example that creates a colorful toggle button. Place this implementation in a separate `.cpp` file to avoid modifying core ImGui sources.

```cpp
// MyToggleButton.h
#pragma once
#include "imgui.h"

namespace ImGui
{
    // Returns true when the toggle changes state.
    bool MyToggleButton(const char* label, bool* v);
}

```

```cpp
// MyToggleButton.cpp
#include "MyToggleButton.h"
#include "imgui_internal.h"   // Required for ButtonBehavior, ItemAdd, etc.

namespace ImGui
{
    bool MyToggleButton(const char* label, bool* v)
    {
        // 1. Generate ID and check visibility
        ImGuiWindow* window = GetCurrentWindow();
        if (window->SkipItems)
            return false;
        
        ImGuiID id = window->GetID(label);

        // 2. Define bounding box
        const ImVec2 label_size = CalcTextSize(label, nullptr, true);
        const ImVec2 padding = GetStyle().FramePadding;
        const ImRect bb(window->DC.CursorPos, 
                        window->DC.CursorPos + ImVec2(padding.x * 2 + label_size.x,
                                                      padding.y * 2 + label_size.y));
        
        ItemSize(bb, GetStyle().FramePadding.y);
        if (!ItemAdd(bb, id))
            return false;  // Clipped

        // 3. Interaction handling
        bool hovered, held;
        bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
        
        if (pressed)
            *v = !*v;

        // 4. Rendering
        const ImU32 col_bg = *v ? GetColorU32(ImGuiCol_ButtonActive) 
                                : GetColorU32(ImGuiCol_Button);
        const ImU32 col_border = hovered ? GetColorU32(ImGuiCol_BorderShadow) 
                                         : GetColorU32(ImGuiCol_Border);
        
        RenderFrame(bb.Min, bb.Max, col_bg, true, GetStyle().FrameRounding);
        window->DrawList->AddRect(bb.Min, bb.Max, col_border, GetStyle().FrameRounding);
        RenderTextClipped(bb.Min + padding, bb.Max - padding, label, nullptr, 
                         &label_size, ImVec2(0.5f, 0.5f), &bb);
        
        return pressed;
    }
}

```

This implementation automatically integrates with ImGui's **ID stack**, allowing multiple toggles with identical labels to coexist when wrapped in `PushID()`/`PopID()` calls.

## Advanced Custom Widget Examples

### Color Picker Mini-Widget

This widget displays a color square that opens the built-in color picker when clicked. It demonstrates how to trigger popups from custom widgets.

```cpp
bool MyColorPicker(const char* label, ImVec4* col)
{
    ImGuiWindow* window = GetCurrentWindow();
    if (window->SkipItems) 
        return false;

    ImGuiID id = window->GetID(label);
    const float size = GetTextLineHeight() * 2.0f;
    const ImRect bb(window->DC.CursorPos,
                    window->DC.CursorPos + ImVec2(size, size));
    
    ItemSize(bb);
    if (!ItemAdd(bb, id)) 
        return false;

    // Interaction
    bool hovered, held;
    bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
    
    if (pressed)
        OpenPopup(label);

    // Rendering
    RenderFrame(bb.Min, bb.Max, GetColorU32(*col), true, 0.0f);
    RenderFrameBorder(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), 0.0f);

    // Popup content
    bool changed = false;
    if (BeginPopup(label))
    {
        changed = ColorPicker4("##picker", (float*)col, ImGuiColorEditFlags_NoLabel);
        EndPopup();
    }
    
    return changed;
}

```

### Circular Knob Slider

This example creates a radial slider control that updates based on mouse angle while held, showcasing custom geometry processing.

```cpp
#include <cmath>

bool MyKnob(const char* label, float* v, float v_min, float v_max, float radius = 30.0f)
{
    ImGuiWindow* window = GetCurrentWindow();
    if (window->SkipItems) 
        return false;

    ImGuiID id = window->GetID(label);
    const ImVec2 center = window->DC.CursorPos + ImVec2(radius, radius);
    const ImRect bb(center - ImVec2(radius, radius), 
                    center + ImVec2(radius, radius));
    
    ItemSize(bb);
    if (!ItemAdd(bb, id)) 
        return false;

    // Interaction
    bool hovered, held;
    bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
    
    if (held)
    {
        ImVec2 mp = GetIO().MousePos - center;
        float angle = atan2f(mp.y, mp.x);
        float t = (angle + IM_PI) / (2.0f * IM_PI);
        *v = ImLerp(v_min, v_max, t);
        MarkItemEdited(id);
    }

    // Rendering
    ImU32 col_bg = GetColorU32(held ? ImGuiCol_FrameBgActive : ImGuiCol_FrameBg);
    RenderFrame(bb.Min, bb.Max, col_bg, true, radius);
    
    // Indicator line
    float angle = ImLerp(-IM_PI, IM_PI, (*v - v_min) / (v_max - v_min));
    ImVec2 end = center + ImVec2(cosf(angle), sinf(angle)) * radius * 0.8f;
    window->DrawList->AddLine(center, end, GetColorU32(ImGuiCol_SliderGrab), 4.0f);
    
    return held;
}

```

## Key Source Files for Custom Widget Development

When extending ImGui, reference these files to understand the internal architecture:

- **[`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h)**: Public API definitions. All custom widgets must include this header for `ImGui::` namespace functions and types.
- **[`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h)**: Contains `ButtonBehavior()` (line 3766), `ItemAdd()` (line 3616), and `SetItemKeyOwner()` (around line 3602) required for interaction handling and item registration.
- **[`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp)**: Reference implementations of all built-in widgets (`Button`, `SliderFloat`, `Checkbox`, etc.) demonstrating proper ID handling, interaction patterns, and rendering techniques.
- **[`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp)**: Core implementation including `RenderFrame()`, `GetID()` generation (around line 1512), and the main UI loop logic.

## Summary

- **Never modify core files**: Place custom widget implementations in separate [`.cpp/.h`](https://github.com/ocornut/imgui/blob/main/.cpp/.h) files that include [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) and [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h).
- **Follow the four-step pattern**: Generate ID → Define bounds → Call `ButtonBehavior()` → Render with draw lists.
- **Use internal helpers**: `ButtonBehavior()` in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) provides production-ready input handling including navigation, focus, and repeat logic.
- **Register items properly**: `ItemAdd()` enables your widget to participate in ImGui's layout, clipping, and navigation systems.
- **Leverage the ID stack**: `GetID()` automatically respects `PushID()` contexts, allowing safe widget reuse.

## Frequently Asked Questions

### Do I need to modify imgui.cpp to add custom widgets?

No. Dear ImGui is explicitly designed for extension without core modifications. Create a new source file (e.g., [`my_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/my_widgets.cpp)), include [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) and [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h), and implement your widget in the `ImGui` namespace. This ensures compatibility with future ImGui updates.

### Why must I include imgui_internal.h for custom widgets?

While basic usage only requires [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h), [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) exposes essential building blocks like `ButtonBehavior()`, `ItemAdd()`, and `SetItemKeyOwner()` that handle the complex logic of mouse interaction, keyboard navigation, and item registration. These functions are stable enough for widget development but are kept internal to preserve API cleanliness.

### How do I handle keyboard input in a custom widget?

After calling `ItemAdd()`, use `SetItemKeyOwner()` (available in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) around line 3602) to claim ownership of specific keys. Check `ImGui::IsKeyDown()` or `ImGui::IsKeyPressed()` within your widget logic to respond to keyboard events while maintaining focus compatibility.

### Can I store state inside a custom widget function?

For state that persists between frames (like animation values or edit buffers), avoid `static` variables inside the function. Instead, accept a pointer to a user-provided state structure as a parameter, similar to how `ImGui::BeginCombo()` works. This allows multiple instances of the widget to maintain independent states.