# Understanding the ImGui ID Stack System for Resolving Widget ID Collisions

> Master ImGui ID Stack System to prevent widget ID collisions. Learn how PushID and PopID scopes allow identical labels for better UI management.

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

---

**ImGui resolves widget ID collisions by hashing each widget's label against a hierarchical ID stack manipulated via `PushID` and `PopID`, allowing identical labels to coexist in different scopes without global uniqueness requirements.**

The Dear ImGui library (ocornut/imgui) uses a sophisticated ID stack system to manage widget identifiers dynamically. When multiple widgets share the same visible label, the ID stack prevents collisions by incorporating scope-specific context into the final hash calculation. Understanding this mechanism is essential for building robust user interfaces with dynamically generated or nested components.

## How the ImGui ID Stack Works

Every widget in Dear ImGui receives a unique `ImGuiID` computed by hashing its label against the current **ID stack**. This stack-based approach eliminates collisions when identical labels appear in different contexts, such as inside loops or nested UI components.

### Base ID Generation via GetID

All widget creation eventually calls `GetID(label)`, with the core implementation residing in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) at lines 9357-9381. This function hashes the supplied string with the contents of `g.CurrentWindow->IDStack`, an `ImVector<ImGuiID>` that stores the current hierarchical context. The hashing is cumulative: each level of the stack contributes to the final ID, ensuring that the same label produces different hashes under different stack configurations.

### PushID and PopID Operations

The public API provides three overloads of `PushID` to inject scope identifiers, declared in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) at lines 605-608:

```cpp
void PushID(const char* str_id);      // Hash a string identifier
void PushID(const void* ptr_id);      // Hash a pointer address
void PushID(int int_id);              // Hash an integer value

```

Each variant hashes its input before pushing it onto `IDStack`, guaranteeing uniform distribution regardless of input type. `PopID` removes the top entry, restoring the previous scope. These operations are O(1) and introduce negligible overhead.

### PushOverrideID for Pre-Computed Identifiers

For advanced scenarios where you already possess a hashed `ImGuiID`, the internal API offers `PushOverrideID`, declared in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) at line 3471. This function pushes the raw ID directly without additional hashing, useful when integrating with external resource managers or custom hashing schemes.

## Source Code Implementation of the ID Stack System

The mechanics reside in specific locations within the ocornut/imgui repository. In [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) (lines 9357-9381), the `PushID` implementations hash their inputs using `ImHashStr` or bitwise operations before appending to `g.CurrentWindow->IDStack`. The stack itself is a simple `ImVector` that grows and shrinks with push/pop operations.

The demo code in [`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp) (lines 961, 1278, 1318, and others) illustrates typical usage patterns in loops, tree nodes, and table rows.

## Code Examples for Preventing Widget ID Collisions

### Disambiguating Widgets in Loops with PushID

When generating widgets inside loops, identical labels cause collision without scope isolation. Push the loop index to create distinct namespaces:

```cpp
for (int i = 0; i < 5; ++i) {
    ImGui::PushID(i);                 // Each iteration receives unique stack context
    if (ImGui::Button("Delete")) {    // Same visible label, different ID
        // handle deletion of item i
    }
    ImGui::PopID();
}

```

Each button receives a distinct final ID because the hash incorporates the integer `i` from the stack.

### Using Pointer Values as Unique Identifiers

For collections of objects, pushing pointer addresses guarantees uniqueness even with identical string content:

```cpp
struct Item { const char* name; };
Item* items[10];
for (int n = 0; n < 10; ++n) {
    ImGui::PushID(items[n]);           // Hash the pointer address
    ImGui::Text("%s", items[n]->name);
    ImGui::PopID();
}

```

This technique leverages memory addresses as entropy, preventing collisions when `name` strings repeat across items.

### Advanced Usage with PushOverrideID

When working with pre-computed IDs from external systems, avoid double-hashing:

```cpp
ImGuiID custom_id = ImHashStr("MySpecialGroup", 0, seed);
ImGui::PushOverrideID(custom_id);      // Push raw ID without re-hashing
// ... create widgets ...
ImGui::PopID();

```

This pattern appears in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) and is essential when mixing ImGui with custom ID allocation schemes.

### Combining PushID with the ## Suffix Syntax

ImGui supports inline unique identifiers via the `##` suffix hidden from display. Combine both techniques for robust scoping:

```cpp
ImGui::PushID(42);
ImGui::Button("Save##file");   // ##file adds hash material
ImGui::PopID();

```

Both the stack and the suffix contribute to the final hash, though `PushID` generally provides cleaner block-scope management than multiple `##` annotations.

## Summary

- **Hierarchical scoping**: The ID stack creates private namespaces via `PushID` and `PopID`, allowing widget labels to repeat safely across different contexts.
- **Flexible input types**: `PushID` accepts strings, pointers, and integers, hashing all inputs uniformly before storage in the `IDStack`.
- **Zero-cost abstraction**: Stack operations use a simple `ImVector` with O(1) complexity, adding negligible runtime overhead to widget generation.
- **Override capability**: `PushOverrideID` (in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h)) supports pre-hashed identifiers for advanced integration scenarios.
- **Collision immunity**: Proper use of the ID stack prevents focus conflicts, state corruption, and activation errors in dynamic, recursively generated UIs.

## Frequently Asked Questions

### What causes widget ID collisions in ImGui?

Widget ID collisions occur when two active widgets generate identical `ImGuiID` values, typically because they share the same label string without distinct stack contexts. This causes ImGui to treat them as the same interactive element, resulting in shared hover states, simultaneous activation, and incorrect focus behavior.

### How does PushID prevent ID collisions in loops?

`PushID` injects scope-specific entropy (such as a loop index or array pointer) into the ID stack before widget creation. Since `GetID` hashes the widget label against the entire stack contents, each iteration produces a unique final identifier despite identical labels, effectively isolating each widget's state.

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

`PushID` hashes its input (string, pointer, or integer) before pushing onto the stack, suitable for raw data sources. `PushOverrideID`, defined in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) at line 3471, accepts a pre-computed `ImGuiID` and pushes it directly without additional hashing, designed for scenarios where you manage identifier generation externally or need to avoid double-hashing.

### When should I use the ## suffix versus PushID?

The `##` suffix provides quick inline disambiguation for single widgets, while `PushID` offers cleaner, block-scoped namespace management for groups of widgets. Use `##` for occasional uniqueness tweaks and `PushID` for systematic isolation in loops, recursive functions, or reusable UI components.