# How to Create Custom Widgets Using `imgui_internal.h`: The Complete Guide

> Learn to create custom Dear ImGui widgets using imgui_internal.h. Integrate seamlessly with navigation focus & styling for powerful UI elements. Complete guide.

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

---

**The [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) header exposes the low-level item lifecycle system and interaction helpers required to build custom Dear ImGui widgets that fully integrate with navigation, focus, and styling.**

Dear ImGui maintains a strict separation between its stable public API ([`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h)) and the low-level internal machinery housed in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h). While the public API provides high-level convenience functions, the internal API grants direct access to the **item registration system**, **behavior helpers**, and **drawing primitives** that the library uses to implement every built-in widget. Mastering these internal hooks allows you to construct bespoke controls that behave identically to native ImGui components.

## The Internal API Architecture

Creating a custom widget requires orchestrating four core internal systems. Unlike the public API, which handles these steps automatically, the internal API requires you to explicitly manage the widget's identity, boundaries, interaction state, and rendering.

### 1. Generate a Stable ID

Every widget needs a unique `ImGuiID` to track state across frames. Use `ImGui::GetID(label)` or the lower-level `ImHashStr` to generate this identifier based on string labels or pointer values.

### 2. Define the Bounding Box

Construct an `ImRect` that encapsulates the widget's screen position and dimensions. This rectangle drives clipping, hit-testing, and navigation focus calculations.

### 3. Register with ItemAdd

Call **`ItemAdd`** ([`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) line 3478, implemented in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp)) to register the widget with Dear ImGui's current window. This function:
- Stores the item's data in `g.LastItemData`
- Applies clipping so the widget hides when outside the window bounds
- Registers the item for navigation and focus handling

```cpp
bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0);

```

### 4. Handle Interaction with Behavior Helpers

Use dedicated behavior functions to compute hover, active, and pressed states. **`ButtonBehavior`** (declared around line 3766 in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h)) processes mouse and keyboard input, updates navigation flags, and manages the `g.NavId` and `NavActivateId` states. Alternative helpers include `SliderBehavior` and `ComboBehavior` located in [`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp).

### 5. Render via Draw Lists

Retrieve the current window's draw list using `ImGui::GetWindowDrawList()` and issue primitive commands (`AddRectFilled`, `AddText`, `AddCircleFilled`, etc.). Always pull colors from `ImGui::GetStyle().Colors` to respect the active theme.

## Step-by-Step Implementation Guide

### Creating IDs and Bounding Boxes

Begin by checking if the window is skipping items (optimization for clipped content), then calculate your widget's rectangle:

```cpp
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems) return false;

ImGuiID id = ImGui::GetID("MyCustomWidget");
ImVec2 pos = window->DC.CursorPos;
ImVec2 size = ImGui::CalcItemSize(ImVec2(0,0), 100.0f, ImGui::GetFrameHeight());
ImRect bb(pos, pos + size);

```

### Registering the Item

The `ItemAdd` call is mandatory for navigation and focus support. Without this, your widget will not respond to tab ordering or gamepad input:

```cpp
if (!ImGui::ItemAdd(bb, id)) return false;

```

For widgets that don't render every frame (such as drag-and-drop proxies), call **`KeepAliveID(id)`** to prevent the ID from being recycled by Dear ImGui's hash table.

### Processing Input States

`ButtonBehavior` is the workhorse for clickable widgets. It outputs hover and held states while internally handling navigation activation:

```cpp
bool hovered = false, held = false;
ImGui::ButtonBehavior(bb, id, &hovered, &held, 0);
bool clicked = held && ImGui::IsMouseReleased(ImGuiMouseButton_Left);

```

For value-based controls, `SliderBehavior` (defined in [`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp)) provides拖拽 logic, clamping, and keyboard manipulation:

```cpp
float new_value = *value;
bool hovered = false;
ImGui::SliderBehavior(bb, id, &new_value, v_min, v_max, ImGuiSliderFlags_None, &hovered);

```

## Complete Code Examples

### Example 1: Custom Toggle Button

This implementation demonstrates the full lifecycle: ID generation, item registration, behavior handling, and custom rendering with shape primitives.

```cpp
bool CustomToggle(const char* label, bool* v)
{
    // Step 1: ID and window context
    ImGuiID id = ImGui::GetID(label);
    ImGuiWindow* window = ImGui::GetCurrentWindow();
    if (window->SkipItems) return false;

    // Step 2: Bounding box calculation
    ImVec2 pos = window->DC.CursorPos;
    ImVec2 size = ImGui::CalcItemSize(ImVec2(0,0), ImGui::GetFontSize() * 2.0f, ImGui::GetFontSize() * 1.2f);
    ImRect bb(pos, pos + size);

    // Step 3: Register item for navigation/clipping
    ImGui::ItemAdd(bb, id);

    // Step 4: Interaction handling
    bool hovered = false, held = false;
    ImGui::ButtonBehavior(bb, id, &hovered, &held, 0);
    
    // Step 5: State mutation
    bool pressed = held && ImGui::IsMouseReleased(ImGuiMouseButton_Left);
    if (pressed) *v = !*v;

    // Step 6: Rendering
    ImU32 col_bg = ImGui::GetColorU32(*v ? ImGuiCol_ButtonActive : 
                                        held ? ImGuiCol_ButtonActive : 
                                        hovered ? ImGuiCol_ButtonHovered : 
                                        ImGuiCol_Button);
    
    ImDrawList* draw_list = ImGui::GetWindowDrawList();
    draw_list->AddRectFilled(bb.Min, bb.Max, col_bg, window->WindowRounding);
    
    // Draw indicator circle
    float pad = bb.GetHeight() * 0.2f;
    ImVec2 circle_pos = *v ? ImVec2(bb.Max.x - pad - (bb.GetHeight() * 0.3f), bb.GetCenter().y) 
                           : ImVec2(bb.Min.x + pad + (bb.GetHeight() * 0.3f), bb.GetCenter().y);
    draw_list->AddCircleFilled(circle_pos, bb.GetHeight() * 0.3f, IM_COL32(255, 255, 255, 255));

    return pressed;
}

```

### Example 2: Custom Slider with Rail Rendering

This example leverages `SliderBehavior` for complex interaction logic while providing completely custom visuals.

```cpp
bool CustomSliderFloat(const char* label, float* value, float v_min, float v_max)
{
    ImGuiID id = ImGui::GetID(label);
    ImGuiWindow* window = ImGui::GetCurrentWindow();
    if (window->SkipItems) return false;

    ImVec2 size = ImGui::CalcItemSize(ImVec2(0,0), 200.0f, ImGui::GetFrameHeight());
    ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
    
    if (!ImGui::ItemAdd(bb, id)) return false;

    // Use internal slider behavior for drag/keyboard logic
    bool hovered = false;
    float temp_val = *value;
    ImGui::SliderBehavior(bb, id, ImGuiDataType_Float, &temp_val, &v_min, &v_max, "", 0, &hovered);
    
    if (temp_val != *value) *value = temp_val;

    // Custom rendering: horizontal rail
    ImDrawList* draw_list = ImGui::GetWindowDrawList();
    ImRect rail_bb(bb.Min + ImVec2(0, bb.GetHeight() * 0.4f), 
                   bb.Max - ImVec2(0, bb.GetHeight() * 0.4f));
    draw_list->AddRectFilled(rail_bb.Min, rail_bb.Max, 
                            ImGui::GetColorU32(ImGuiCol_FrameBg), 4.0f);

    // Custom rendering: position indicator
    float t = (*value - v_min) / (v_max - v_min);
    float grab_x = ImLerp(rail_bb.Min.x, rail_bb.Max.x, t);
    ImVec2 grab_center(grab_x, rail_bb.GetCenter().y);
    draw_list->AddCircleFilled(grab_center, bb.GetHeight() * 0.4f, 
                              ImGui::GetColorU32(hovered ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab));

    // Label rendering
    ImGui::RenderTextClipped(bb.Min, bb.Max, label, NULL, NULL, ImVec2(0.5f, 0.5f));

    return hovered;
}

```

## Key Source Files to Reference

Understanding the internal implementation requires studying these specific files in the `ocornut/imgui` repository:

- **[`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h)** – Declares `ItemAdd`, `ButtonBehavior`, `KeepAliveID`, and the navigation flag constants (e.g., `ImGuiButtonFlags_NoNavFocus`).
- **[`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp)** – Contains the implementations of `ItemAdd` and the core item lifecycle logic that manages `g.LastItemData` and clipping.
- **[`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp)** – Houses the concrete behavior implementations including `SliderBehavior`, `ComboBehavior`, and the detailed logic for `ButtonBehavior`.
- **[`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_draw.cpp)** – Provides the `ImDrawList` primitives (`AddRect`, `AddText`, etc.) used for custom rendering.
- **[`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp)** – Contains practical demonstrations of advanced rendering techniques and examples of how the internal API constructs complex widgets.

## Summary

- **The internal API** in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) exposes the building blocks used by Dear ImGui's own widget suite, enabling fully native custom controls.
- **ItemAdd** is mandatory for navigation, focus, and clipping support; it registers your widget's bounding box and ID with the current window context.
- **ButtonBehavior** and **SliderBehavior** handle complex input processing, navigation activation, and state management automatically.
- **KeepAliveID** prevents ID collision for widgets that skip frames or exist only during specific interaction states.
- Custom widgets should draw using `ImDrawList` primitives and reference `ImGui::GetStyle().Colors` to maintain visual consistency with the user's theme.

## Frequently Asked Questions

### When should I use [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) instead of the public API?

Use the internal API when you need **custom geometry** or **specialized interaction patterns** that the standard `Button()`, `Slider()`, or `InputText()` functions cannot accommodate. The internal API is essential for widgets requiring non-rectangular hit areas, composite controls, or unique rendering pipelines. Note that because these symbols are not guaranteed stable across versions, you should pin your project to a specific Dear ImGui version when using internals.

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

**`ItemAdd`** registers the widget's existence with Dear ImGui's window and navigation systems, handling clipping and focus allocation. **`ButtonBehavior`** processes input events (mouse clicks, keyboard navigation, gamepad) and calculates hover/active states. You must call `ItemAdd` before `ButtonBehavior` to establish the widget's identity and bounding box, but `ButtonBehavior` is only required if your widget needs click or hover detection.

### Do custom widgets built with the internal API support gamepad navigation?

Yes. Because `ItemAdd` populates the internal `LastItemData` structure and `ButtonBehavior` respects the `NavId` system, widgets built using these primitives automatically participate in Dear ImGui's keyboard and gamepad navigation. The behavior functions internally set `g.NavActivateId` and handle `ImGuiButtonFlags_NoNavFocus` flags, ensuring your custom widget responds identically to built-in controls when using tab navigation or directional pads.

### How do I handle text input inside a custom widget?

For widgets requiring text entry, use **`InputTextEx`** (declared in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) and implemented in [`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp)) rather than building raw input handling. This internal function exposes the full text editing state machine, undo/redo buffers, and callback system while allowing you to control the rendering. Alternatively, call `ImGui::InputText()` publicly and overlay custom graphics using `GetWindowDrawList()` if you only need visual customization rather than behavioral changes.