# How to Manage Widget IDs and Prevent Collisions in Dear ImGui Using the ID Stack

> Learn to manage widget IDs and prevent collisions in Dear ImGui using the ID stack. Master PushID and PopID for organized UI development and avoid common errors.

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

---

**Dear ImGui generates distinct 32-bit `ImGuiID` values by hashing the current ID stack, which developers manipulate via `PushID()` and `PopID()` calls to scope widgets and eliminate collisions when multiple items share identical visible labels.**

Every interactive element in Dear ImGui (the ocornut/imgui repository) requires a unique identifier to track focus, hover states, and persistent data. The library derives these IDs by hashing a runtime stack of identifiers that you control. When you need to manage widget IDs and prevent collisions in Dear ImGui using the ID stack, you are essentially manipulating this hierarchical hashing system to ensure every button, slider, or tree node receives a distinct signature even when their visual labels are identical.

## Understanding the ID Stack Architecture

Dear ImGui uses a **32-bit `ImGuiID`** to distinguish every interactable widget. This identifier is not assigned manually but computed by hashing the **ID stack**—a dynamic array of string pointers, memory addresses, and integers that you push and pop at runtime.

As implemented in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) (lines **9357–9381**), the core API provides three overloads of `PushID()` and a corresponding `PopID()`:

- **`ImGui::PushID(const char* str_id)`** – Hashes a null-terminated string onto the stack.
- **`ImGui::PushID(const void* ptr_id)`** – Hashes a raw pointer (useful for object addresses).
- **`ImGui::PushID(int int_id)`** – Hashes a 32-bit integer (ideal for loop indices).
- **`ImGui::PopID()`** – Removes the top-most identifier, restoring the previous stack state.

The final ID for any widget is the cumulative hash of all items currently in this stack combined with the widget's visible label (or hash of "##" hidden labels). This design allows multiple widgets to share the same display text without conflict, provided they exist within different ID scopes.

## Preventing Collisions: The PushID/PopID Workflow

Collisions occur when two active widgets produce identical `ImGuiID` values, causing ImGui to merge their states—resulting in flickering focus, shared hover detection, or lost interaction. This happens most frequently when generating widgets inside loops or recursive functions where visible labels repeat.

### The Three-Step Safety Pattern

To guarantee uniqueness, wrap related widgets with a scoped identifier:

1. **Push a unique base** before creating the group. Use the loop index, object pointer, or a descriptive string.
2. **Create widgets** using short or repeating labels (often with `##` hidden identifiers).
3. **Pop the identifier** immediately after the group ends to prevent scope leakage.

According to the source code in [`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp) (line **1593**), the library validates stack integrity with `IM_ASSERT_USER_ERROR`, ensuring every `PushID` has a matching `PopID` when tables or scopes exit.

## Practical Implementation Patterns

### Loop Iteration with Integer IDs

When iterating containers, push the array index to distinguish otherwise identical buttons:

```cpp
for (int i = 0; i < items.size(); i++) {
    ImGui::PushID(i);  // Unique per iteration
    if (ImGui::Button("Delete")) {
        items.erase(items.begin() + i);
    }
    ImGui::PopID();
}

```

This pattern appears throughout [`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp) (e.g., lines **961**, **1318**, and **3463**), demonstrating how integer IDs isolate dynamic list elements.

### Object Pointers for Hierarchical Trees

For tree nodes or recursive structures, use the object's memory address as the identifier:

```cpp
void DrawNode(Node* node) {
    ImGui::PushID(node);  // Unique per object instance
    bool open = ImGui::TreeNode("##Node", "%s", node->name.c_str());
    ImGui::PopID();
    
    if (open) {
        for (Node* child : node->children) {
            DrawNode(child);
        }
        ImGui::TreePop();
    }
}

```

As seen in [`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp) (lines **2910–2920**), internal tree widget implementations rely on this pointer-based approach to handle duplicate display names across different hierarchy levels.

### Reusable Components with String Scopes

When building composable widgets that might appear multiple times in the same window, push a descriptive string:

```cpp
void DrawColorEditor(const char* context_id, ImVec4* color) {
    ImGui::PushID(context_id);  // e.g., "PlayerHUD" vs "EnemyHUD"
    ImGui::ColorEdit4("##Color", (float*)color);
    ImGui::PopID();
}

```

Internal Dear ImGui widgets such as `ColorEdit` and `Slider` use similar stack manipulation, visible in [`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp) at lines **844** and **1230**, to ensure nested composite controls do not collide with sibling elements.

## Debugging and Validation in the Source Code

When IDs collide, Dear ImGui provides debugging tools to inspect the active stack. The **Metrics/Debug** window (accessible via `ImGui::ShowMetricsWindow()`) displays the full ID stack for the hovered widget. This tooltip logic is implemented in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) at line **18512** within the `MetricsHelpMarker` functionality.

If you encounter assertion failures, check [`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp) line **1593** where `IM_ASSERT_USER_ERROR` validates that ID stack depth matches expected scope boundaries, particularly when closing tables or menus. Mismatched `PushID`/`PopID` pairs trigger these diagnostics immediately.

## Summary

- **Dear ImGui** assigns every widget a 32-bit `ImGuiID` computed by hashing the current ID stack combined with the label.
- **PushID** accepts strings, pointers, or integers to inject uniqueness into the hash, while **PopID** restores the previous scope.
- **Always wrap loops**, recursive trees, and reusable components with `PushID`/`PopID` pairs to prevent collisions between widgets sharing visual labels.
- **Validate your code** using the Metrics window (line **18512** in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp)) and ensure matching push/pop calls to avoid assertions at `imgui_tables.cpp:1593`.

## Frequently Asked Questions

### What happens if two Dear ImGui widgets have the same ID?

When two active widgets share the same `ImGuiID`, Dear ImGui treats them as the same logical item. This causes focus, hover, and activation states to conflict, resulting in flickering inputs, stuck buttons, or lost interactions. The library requires every interactable element to possess a unique identifier derived from the ID stack and label combination.

### Can I use the ID stack with Dear ImGui tables and columns?

Yes. Dear ImGui automatically manipulates the ID stack internally when creating tables to isolate cell contents. As shown in [`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp) at line **1593**, the table system validates stack depth with `IM_ASSERT_USER_ERROR` to ensure that row and cell identifiers do not leak outside table boundaries. You should still push your own IDs when placing interactive widgets inside table cells.

### Is PushID(int) or PushID(void*) better for performance?

Both `PushID(int)` and `PushID(const void*)` generate equivalent hashing overhead because Dear ImGui hashes the binary representation of the pointer or integer value directly. According to the implementation in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) (lines **9357–9381**), choose **pointers** for stable object references (like tree nodes) and **integers** for transient loop indices. Avoid hashing large strings when possible, as pointer and integer hashes involve less computation.

### How do I debug which ID stack entry is causing a collision?

Open the **Metrics/Debugger** window using `ImGui::ShowMetricsWindow()` and hover over the problematic widget. The tooltip displays the full ID stack hash and scope depth, implemented in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) at line **18512**. Compare the stack entries between colliding widgets to identify where you failed to push a unique identifier or missed a `PopID` call.