# Avoiding ID Collisions in Dear ImGui: Complete Guide to Widget ID Management

> Master Dear ImGui widget ID management. Learn best practices to avoid label collisions and ensure unique IDs with PushID PopID and hidden suffixes.

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

---

**Dear ImGui generates unique widget identifiers by hashing the visible label combined with the current ID stack; collisions occur when multiple widgets share identical labels within the same scope, and you resolve them by appending hidden `##` suffixes or wrapping calls with `ImGui::PushID()` and `ImGui::PopID()`.**

When building complex user interfaces with the **ocornut/imgui** immediate-mode library, developers frequently encounter ID collisions that cause widgets to share state, focus, and interaction incorrectly. This happens because Dear ImGui derives every interactive element's identifier using the formula `final_id = hash(all_id_stack_entries + visible_label)`, meaning identical labels in identical scopes produce identical hashes. Understanding how to manipulate the ID stack is essential for creating robust interfaces with duplicate labels or dynamically generated elements.

## Why Collisions Happur in Dear ImGui

ID collisions manifest when two widgets receive the same hash value, causing the UI library to treat them as a single underlying element. This commonly occurs in two scenarios:

- **Static duplicates**: Multiple `ImGui::Button("Play")` calls within the same window without additional scope differentiation
- **Dynamic generation**: Loops that create identical widgets without pushing distinguishing identifiers onto the stack

The collision mechanism is evident in the core implementation within [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) (lines 9355-9380), where the `PushID` functions manipulate an internal stack that gets hashed alongside every widget label.

## Three Reliable Strategies for Avoiding ID Collisions

Dear ImGui provides three complementary approaches to ensure unique identifiers, all documented in the official FAQ (lines 88-106 and 126-138).

### Strategy 1: Append Hidden ## Suffixes to Labels

The `##` syntax allows you to append invisible text to a label that contributes to the ID hash without affecting the visible interface. The text following `##` is hashed into the ID but never rendered.

```cpp
ImGui::Begin("Audio Controls");
ImGui::Button("Play");                 // ID = hash("Audio Controls", "Play")
ImGui::Button("Play##Track1");        // ID includes "Play##Track1"
ImGui::Button("Play##Track2");        // ID includes "Play##Track2"
ImGui::End();

```

Use this approach when the number of widgets is known at compile-time or when you can manually assign unique names to each instance.

### Strategy 2: Wrap Widgets with PushID/PopID

The most robust method for dynamic content involves explicitly pushing distinguishing values onto the ID stack before creating widgets. As implemented in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp), `PushID()` accepts integers, pointers, or strings.

```cpp
ImGui::Begin("Object List");
for (int i = 0; i < objects.size(); ++i) {
    // Prefer stable identifiers over loop indices to survive reordering
    ImGui::PushID(objects[i]->Name);   // or PushID(objects[i])
    ImGui::Button("Select");           // Visible label remains "Select"
    ImGui::PopID();
}
ImGui::End();

```

According to the FAQ (lines 126-138), this is the "most convenient way of distinguishing ID when iterating and creating many UI elements programmatically." Tree nodes automatically perform this push operation, which is why nesting widgets under `TreeNode()` calls prevents collisions without manual intervention.

### Strategy 3: Combine Both Approaches for Complex Hierarchies

For intricate interfaces with multiple nesting levels, combine `PushID` scopes with `##` suffixes to create redundant disambiguation layers.

```cpp
ImGui::Begin("Scene");
for (auto &node : scene.Nodes) {
    ImGui::PushID(node);                     // Unique per node object
    if (ImGui::TreeNode("##Node")) {        // Hidden label keeps tree node unique
        ImGui::Button("Delete##Btn");       // ID = hash(node, "Delete##Btn")
        ImGui::TreePop();
    }
    ImGui::PopID();
}
ImGui::End();

```

This pattern appears in [`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp) (lines 955-973), which demonstrates proper ID handling in the built-in demo window.

## Implementation Details from Source Code

The architecture behind Dear ImGui's ID system resides in several key files:

- **[`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp)** (lines 9355-9380): Contains the overloaded `PushID` implementations that hash integers, pointers, and strings into stack entries
- **[`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h)**: Declares the public API for `PushID`/`PopID` and documents label conventions
- **[`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp)** (lines 955-973): Provides working examples of ID stack manipulation in loops and tree structures
- **[`docs/FAQ.md`](https://github.com/ocornut/imgui/blob/main/docs/FAQ.md)** (lines 88-106, 145-158): Authoritative documentation stating that using `##` "solves simple collision cases" while `PushID`/`PopID` creates scopes for programmatic UI generation

When using empty labels, always include a `##` prefix with unique text (e.g., `"##unique"`) to ensure the widget receives a valid hash rather than a collision-prone empty string.

## Summary

- **ID collisions occur** when identical labels share the same ID stack scope, causing widgets to share state and focus
- **The ## suffix** adds hidden uniqueness to labels at compile-time without changing the visible text

- **PushID/PopID** creates runtime scopes using integers, pointers, or strings, making it ideal for dynamic lists and loops
- **Stable identifiers** (object pointers or names) survive reordering better than array indices
- **Tree nodes** automatically manipulate the ID stack, providing implicit scope separation for nested widgets

## Frequently Asked Questions

### What causes ID collisions in Dear ImGui?

ID collisions occur when two interactive widgets generate identical hash values, which happens when they share the same visible label within the same ID stack scope. Because Dear ImGui computes `final_id = hash(stack_entries + label)`, any combination of matching labels and matching stack contexts produces the same identifier, forcing the UI to treat distinct visual elements as a single logical entity.

### How do I handle widget IDs inside loops?

Wrap your widget creation calls with `ImGui::PushID()` and `ImGui::PopID()`, passing a stable unique identifier such as an object pointer or name string rather than the loop index. According to the [`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp) examples, using indices alone risks losing widget state if the collection gets reordered, whereas stable identifiers maintain consistent ID-to-object mapping across frames.

### Can I use completely invisible labels in Dear ImGui?

Yes. Use the `"##uniqueidentifier"` syntax where the text following `##` provides the unique hash input while rendering nothing visible to the user. This technique is documented in the FAQ (lines 88-106) as the standard method for creating invisible but distinct widgets, particularly useful for icon buttons or spacing elements.

### Do built-in widgets like TreeNode affect the ID stack?

Yes. Tree nodes, tabs, and other container widgets automatically call `PushID()` internally when opened, creating a new scope for all child widgets. This automatic scoping is why widgets with identical labels placed under different tree nodes do not collide, even without manual `PushID` calls.