Skip to main content

Why IDs Matter

Dear ImGui needs to uniquely identify UI elements internally to:
  • Track active widgets (which slider is being dragged)
  • Track focus state (which input field has keyboard focus)
  • Remember window state (position, size, collapsed state)
  • Remember tree node state (which nodes are open/closed)
  • Associate temporary state with widgets
Non-interactive widgets like Text() don’t need IDs. Interactive widgets like Button(), SliderFloat(), InputText() etc. all need unique IDs.

How IDs Are Generated

IDs are hashed from a stack of identifiers that forms a “path” to each widget:
The same label in different windows produces different IDs because the window name is part of the ID path.

The Most Common Mistake

Using the same label multiple times in the same scope is the #1 beginner mistake!
What happens: Only the first widget responds to input. The others are broken.

Solution 1: Unique Labels

Use different labels:

Solution 2: The ## Operator

Use ## to add a hidden unique suffix:
The ## and everything after it is used for the ID but not displayed.

Creating Hidden Widgets

Solution 3: PushID() / PopID()

The most powerful and flexible approach - add custom ID components to the stack:
Each iteration has a unique ID scope:
  • Iteration 0: hash("Window", 0, "Position"), hash("Window", 0, "Color")
  • Iteration 1: hash("Window", 1, "Position"), hash("Window", 1, "Color")
  • Iteration 2: hash("Window", 2, "Position"), hash("Window", 2, "Color")

PushID() Variants

You can push different types:

Example: Object List

Nested ID Scopes

You can nest PushID() calls:

Tree Nodes and ID Stack

Tree nodes automatically push an ID when opened:
This is why you can have duplicate labels in different tree nodes:

The ### Operator

Use ### to change the label while keeping the same ID:
Everything before ### is the label. Everything after (including ###) is used for the ID:
This is useful for animated or dynamic labels:

Empty Labels

An empty label uses the parent’s ID, which almost always causes conflicts!

Debugging ID Conflicts

Dear ImGui provides tools to debug ID issues:

ID Stack Tool Window

This window shows:
  • The current ID stack path
  • The hash value at each level
  • Which widget corresponds to which ID
Hover over widgets to see their ID path!

Using the Demo

Common Patterns

Pattern 1: Dynamic Lists

Pattern 2: Property Grids

Pattern 3: Tabs/Pages

Advanced: Manual ID Management

GetID() Function

You can manually compute IDs:

Comparing IDs

ID Stack Rules Summary

  1. Always use unique labels in the same scope
  2. Use ##hidden_id for unique IDs with same visible label
  3. Use ###persistent_id for dynamic labels with stable IDs
  4. Use PushID()/PopID() when iterating or creating dynamic UI
  5. Never use empty labels without ##
  6. Windows automatically push their name to the ID stack
  7. Tree nodes automatically push their label when open
  8. Always pair PushID() with PopID()

Window IDs and .ini Files

Window IDs affect saved settings:

Performance Considerations

ID hashing is very fast. Don’t worry about performance when using PushID():
  • Integer hash: ~1-2 CPU cycles
  • String hash: ~2-3 cycles per character
  • Pointer hash: ~1-2 CPU cycles
The ID stack is optimized and has minimal overhead.

Common Errors and Fixes

Error: “Widget not responding to clicks”

Cause: ID conflict - another widget has the same ID Fix: Use unique labels or PushID()

Error: “Wrong widget is activated”

Cause: Multiple widgets share the same ID Fix: Add ##unique_suffix or use PushID()

Error: “Tree node state not saved”

Cause: Tree node ID changes each frame Fix: Use ###persistent_id for dynamic tree labels

Error: “Window position not saved”

Cause: Window name changes each frame Fix: Use ###WindowID for dynamic window titles

Next Steps