# How the Dear ImGui ID System Works and How to Avoid ID Collisions

> Understand the Dear ImGui ID system to prevent collisions. Learn how hashing and scoping with PushID PopID ensures unique identifiers for your UI elements.

- Repository: [omar/imgui](https://github.com/ocornut/imgui)
- Tags: internals
- Published: 2026-07-14

---

**The Dear ImGui ID system generates unique 32-bit `ImGuiID` values by hashing the current ID stack with a user-supplied seed (string, pointer, or integer), and you can prevent collisions by scoping repeating labels with `PushID`/`PopID` or using pointer-based identifiers for dynamic objects.**

Every interactive widget in Dear ImGui (ocornut/imgui) requires a unique identifier to track state, handle interactions, and manage persistent storage. The **Dear ImGui ID system** computes these identifiers as deterministic hashes of the current ID stack combined with user-provided seeds, making it crucial to understand how **ID collisions** occur when multiple widgets share the same label within the same scope.

## How Dear ImGui Generates 32-bit IDs

The core identifier generation logic resides in `ImGuiWindow::GetID()` within [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) (lines 2827-2831). When you call any widget function, ImGui concatenates the current ID stack with your supplied seed and runs it through the `ImHashStr` function—a Murmur-like 32-bit hash algorithm.

```cpp
// Simplified view from imgui.cpp
ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) {
    ImGuiID seed = ImHashStr(str, str_end ? (int)(str_end - str) : 0, 0);
    return ImHashStr(&IDStack[0], IDStack.Size * sizeof(ImGuiID), seed);
}

```

### The ID Stack Mechanism

Each window maintains an ID stack (`ImGuiWindow::IDStack`) that scopes all subsequent ID calculations. Whenever ImGui enters a new scope via `Begin()`, `BeginChild()`, or `PushID()`, it pushes a hashed seed onto this stack. The final `ImGuiID` for any widget is the hash of the **entire stack contents** combined with the widget's specific seed.

### Public API for ID Generation

The public interface exposes several overloads declared in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) (lines 610-614) that delegate to `ImGuiWindow::GetID`:

- **`GetID(const char* str_id)`** – Uses the string itself as the seed (most common for widget labels)
- **`GetID(const void* ptr_id)`** – Uses the pointer value for stable objects (e.g., `&myStruct`)
- **`GetID(int int_id)`** – Uses integer values for small, predictable IDs
- **`GetIDFromPos(const ImVec2& p_abs)`** – Generates IDs based on absolute screen coordinates
- **`GetIDFromRectangle(const ImRect& r_abs)`** – Creates region-based identifiers from rectangle corners

All overloads eventually route through `GetIDWithSeed` ([`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) lines 3470-3471), which allows explicit seed injection used by `PushID`/`PopID` pairs.

## Common Causes of ID Collisions

ID collisions occur when two widgets generate identical 32-bit hashes within the same window scope. The most common mistake is reusing the same label text inside a loop without scoping:

```cpp
// WRONG: Both buttons will have the same ID
for (int i = 0; i < 3; i++) {
    if (ImGui::Button("Delete")) { /* ... */ }  // Collision!
}

```

Since both buttons compute the same hash value, Dear ImGui treats them as the same widget, causing the second button to be unresponsive and share state with the first.

## How to Prevent ID Collisions

### Use PushID and PopID for Iterated Content

Wrap dynamic content in `PushID`/`PopID` pairs to inject uniqueness into the ID stack. This is the standard approach for lists and grids:

```cpp
// Example from imgui_demo.cpp - List with unique IDs
for (int i = 0; i < items.Size; ++i) {
    ImGui::PushID(i);                     // Unique seed per row
    if (ImGui::Button("Delete")) {
        DeleteItem(i);
    }
    ImGui::SameLine();
    ImGui::Text("%s", items[i].c_str());
    ImGui::PopID();
}

```

### Prefer Pointer-Based IDs for Dynamic Objects

For heap-allocated or dynamically created widgets, pass the object pointer directly to guarantee uniqueness:

```cpp
MyWidget* w = GetWidget();
ImGui::SliderFloat("Size", &w->size, 0.1f, 10.0f);
ImGui::Button("Reset", ImGui::GetID(w)); // Unique per object instance

```

This technique leverages `GetID(const void* ptr_id)` and ensures that distinct objects never collide regardless of their label text.

### Hide Labels While Preserving ID Uniqueness

Use the `##` syntax to hide label text from the UI while keeping it part of the ID calculation:

```cpp
ImGui::Button("##node1");  // Visible text is empty, but ID includes "node1"
ImGui::Button("##node2");  // Distinct ID because seed differs

```

### Avoid Mixing Implicit and Explicit ID Scopes

If you `PushID("Menu")` and then call `Button("Item")`, the final ID computes as `hash("MenuItem")`. Changing the push value will change all child IDs, which is intentional but can cause accidental collisions if the push value is not unique across scopes.

### Geometry-Based ID Considerations

When using `GetIDFromPos` or `GetIDFromRectangle`, ensure the geometry is unique per item (e.g., include a per-item offset). Two identical rectangles at the same screen location will generate identical IDs, causing interaction conflicts.

## Implementation Details

The ID system implementation spans several key files in the ocornut/imgui repository:

- **[`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h)** (lines 2827-2831, 3470-3471) – Contains `ImGuiWindow::GetID*` overloads and `GetIDWithSeed`
- **[`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp)** – Implements `ImHashStr` (the Murmur-like hash) and ID stack manipulation logic
- **[`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp)** – Demonstrates how widgets call `GetID` to obtain their identifiers
- **[`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp)** – Provides Working examples of `PushID`/`PopID` usage patterns

The stack-based architecture ensures that IDs remain stable across frames as long as the hierarchy is consistent, enabling Dear ImGui to track widget state persistently without requiring user-managed GUIDs.

## Summary

- **Dear ImGui IDs are 32-bit hashes** computed from the current ID stack combined with a user-supplied string, pointer, or integer seed
- **Collisions occur** when identical labels appear in the same scope without differentiation, causing widgets to share state and input handling
- **Use `PushID`/`PopID`** to isolate loops and dynamic content, or pass explicit pointer/integer IDs to `GetID()` overloads
- **The `##` suffix** hides labels from display while preserving their contribution to the ID hash
- **Source implementation** resides in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) and [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp), utilizing `ImHashStr` for deterministic hashing

## Frequently Asked Questions

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

When two widgets generate identical `ImGuiID` values within the same window, Dear ImGui treats them as the same underlying widget. The second widget will not respond to interactions independently, and both will share the same state (hover, active, and persistent storage). This manifests as "frozen" buttons or sliders that seem to control each other.

### How do I create unique IDs for items in a loop?

Wrap the loop body with `ImGui::PushID(i)` and `ImGui::PopID()` where `i` is your loop index. This pushes the integer onto the ID stack, ensuring that subsequent widgets (like `Button("Delete")`) compute unique hashes for each iteration. Alternatively, use pointer-based IDs with `ImGui::GetID(&your_array[i])` if your data structure provides stable memory addresses.

### Can I use the same label text for different buttons?

Yes, but only if they exist in different ID scopes. You can use the same label in different windows, different `BeginChild` sections, or within different `PushID`/`PopID` blocks. Within the same scope, you must differentiation them using the `##` suffix (e.g., `Button("OK##1")` and `Button("OK##2")`) or by calling `PushID` before each button.

### What is the difference between PushID and GetID?

`PushID` modifies the current window's ID stack by pushing a new seed, affecting all subsequent widget IDs until `PopID` is called. `GetID` simply returns the computed `ImGuiID` for a given seed without altering the stack state. Use `PushID` when creating scoped regions with multiple widgets, and use `GetID` when you need to obtain an ID value for explicit storage or to pass into low-level API functions.