# How Dear ImGui's ID Stack System Works to Prevent Collisions

> Learn how Dear ImGui's ID stack system prevents widget collisions. Discover how PushID and PopID create unique identifiers for seamless UI development.

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

---

**Dear ImGui computes unique widget identifiers by hashing the current ID stack—a dynamically managed list of scopes pushed via `PushID()` and popped via `PopID()`—combined with the widget's label, ensuring no two interactive elements collide even when labels overlap.**

The immediate mode GUI paradigm used by Dear ImGui requires a robust mechanism to track widget state across frames. The library solves this through a hierarchical ID stack system that lives inside `ImGuiContext`, automatically generating unique hashes by combining scope identifiers with user-provided labels. Understanding how this system operates is essential for debugging focus issues and managing complex nested interfaces in the `ocornut/imgui` codebase.

## Understanding the ID Stack Architecture

The ID stack is a dynamic array stored within `ImGuiContext` that maintains the current hierarchical scope. Rather than storing explicit identifiers, Dear ImGui computes them on-the-fly by hashing the contents of this stack.

### Automatic Scope Management

When you call functions that create new scopes—such as `ImGui::Begin()` or `ImGui::TreeNode()`—the library internally executes `PushID()` for you. The stack is subsequently cleared with `PopID()` when the scope ends (for example, when matching `ImGui::End()` is called). This automatic management ensures that widgets nested inside different windows or tree nodes receive distinct identifiers without manual intervention. The core implementation of this mechanism resides in the **ID STACK section** of [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) around line 83.

## PushID Overloads and Value Types

You can manually influence the ID stack using the `PushID` function, which offers four overloads defined in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) between lines 9355 and 9379. These allow you to push three distinct value types onto the stack:

- **`PushID(const char* str_id)`** – Hashes a null-terminated string
- **`PushID(const char* str_begin, const char* str_end)`** – Hashes a string range
- **`PushID(const void* ptr_id)`** – Uses a pointer value (useful for object-based identification)
- **`PushID(int int_id)`** – Uses an integer value (common for array indices)

The value you push becomes the next element in the stack. The final ID for any widget is the hash of **all elements** currently on the stack combined with the widget's own label string.

## Label Syntax and ID Manipulation

Dear ImGui's label syntax provides two special operators that modify how the ID hash is constructed without changing the visible text:

- **`##`** (double hash) – Adds a hidden suffix to the string that participates in the hash calculation but is not rendered to the screen. This allows multiple buttons with the same visible label to coexist by appending unique identifiers after `##`.

- **`###`** (triple hash) – Excludes everything preceding it from the hash calculation, using only the text that follows. This lets you change the visible text dynamically while maintaining a stable ID for state persistence.

The canonical documentation for these operators appears in [`docs/FAQ.md`](https://github.com/ocornut/imgui/blob/main/docs/FAQ.md) under the section **About the ID Stack system**.

## How GetID Generates the Final Hash

Internally, Dear ImGui calls `GetID()` (defined near the ID STACK section in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp)) to compute the final identifier. This function iterates over the current stack, mixing each element into a 32-bit hash using the same algorithm employed for string hashing. The resulting hash is stored in the widget's `ID` field and serves as the key for all state look-ups, including focus, activation, and hover detection.

## Practical Implementation Examples

The following patterns demonstrate how to leverage the ID stack system in production code:

### Integer-Based Loop Scoping

```cpp
ImGui::Begin("MyWindow");
for (int i = 0; i < 3; ++i) {
    ImGui::PushID(i);                // Stack: ["MyWindow", i]
    ImGui::Button("Click");          // ID = hash("MyWindow", i, "Click")
    ImGui::PopID();
}
ImGui::End();

```

### Pointer-Based Object Identification

```cpp
MyObject* obj = GetSelectedObject();
ImGui::PushID(obj);                  // Stack: ["MyWindow", obj]
ImGui::Checkbox("Enabled", &obj->enabled);
ImGui::PopID();

```

### Hidden Suffix for Duplicate Labels

```cpp
// Same visible text, different IDs
ImGui::Button("Play##song42");       // Stack: ["MyWindow"]; ID = hash("MyWindow", "Play##song42")
ImGui::Button("Play##song43");       // Different ID due to hidden suffix

```

### Dynamic Labels with Stable IDs

```cpp
// Visible text changes, but ID remains constant
float fps = ImGui::GetIO().Framerate;
char buf[64];
sprintf(buf, "FPS: %.1f###FPSLabel", fps);
ImGui::Text(buf);                    // ID always equals hash("MyWindow", "FPSLabel")

```

## Debugging ID Collisions

Dear ImGui provides a visual debugging tool to inspect the ID stack hierarchy. You can access it programmatically via `ImGui::ShowIDStackToolWindow()` or through the demo interface at **Demo > Tools > ID Stack Tool**. This utility displays every level of the current stack along with intermediate hash values, making it invaluable for troubleshooting why two widgets might be sharing state or why focus is behaving unexpectedly.

## Summary

- The ID stack lives within `ImGuiContext` and automatically scopes widget identity through `PushID` and `PopID` pairs.
- `PushID` supports string, pointer, and integer overloads located in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) at lines 9355-9379.
- Label syntax uses `##` for hidden hash suffixes and `###` to exclude preceding text from the hash calculation.
- `GetID` iterates the entire stack to produce a 32-bit hash that serves as the widget's unique identifier.
- Use `ShowIDStackToolWindow()` from [`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp) to visualize stack levels and debug collision issues.

## Frequently Asked Questions

### What happens if I forget to call PopID?

Forgetting to call `PopID()` creates a stack imbalance that persists until the end of the frame. In development builds, Dear ImGui will assert when the window or scope ends if the stack depth doesn't match the expected value. Unbalanced stacks cause subsequent widgets to inherit incorrect scope prefixes, leading to ID collisions where unrelated widgets share the same state and input handling.

### Can I use the same label for multiple buttons in different windows?

Yes. The `ImGui::Begin()` function automatically pushes the window identifier onto the ID stack, ensuring that identical labels in different windows produce distinct hashes. This hierarchical scoping means you rarely need to manually manage IDs unless creating multiple identical widgets within the same container.

### How do I debug ID collisions in Dear ImGui?

Enable the ID Stack Tool by calling `ImGui::ShowIDStackToolWindow()` or navigating to **Demo > Tools > ID Stack Tool** in the demo window. This tool displays the current stack hierarchy and the resulting hash values for each level, allowing you to see exactly why two widgets are colliding and which `PushID` calls are contributing to their final identifiers.

### What is the difference between ## and ### in labels?

The `##` operator adds hidden text that participates in the hash but is not rendered, allowing unique IDs while maintaining identical visible labels. The `###` operator excludes everything preceding it from the hash calculation, using only the text after the triple hash as the identifier. Use `###` when you want to change the visible text dynamically (such as displaying changing values) while keeping the ID stable for state persistence.