# Dear ImGui vs Traditional Retained-Mode UI: Architecture, Performance, and Code Comparison

> Compare Dear ImGui and traditional retained-mode UI architectures. Understand immediate-mode GUI against persistent widget trees for efficient application development.

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

---

**Dear ImGui implements an immediate-mode GUI (IMGUI) paradigm where the user interface is rebuilt every frame with application-owned state, while traditional retained-mode toolkits maintain persistent widget trees with library-owned state and callback-driven interactions.**

The `ocornut/imgui` repository provides a bloat-free graphical user interface library designed for rapid iteration and easy integration into game engines or visualization tools. Unlike conventional UI frameworks that rely on object-oriented widget hierarchies, Dear ImGui inverts control by rendering the entire interface on-the-fly each frame. This architectural distinction fundamentally changes how developers handle state, layout calculations, and rendering integration.

## Core Architectural Differences

### Immediate-Mode Philosophy

In Dear ImGui, the UI does not exist as persistent objects between frames. Instead, the application issues draw commands immediately inside the main loop. According to the source code comments in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) around line 213, Dear ImGui explicitly declares itself "an implementation of the IMGUI paradigm (immediate-mode graphical user interface)."

Each widget call returns interaction state directly. For example, `bool pressed = ImGui::Button("OK");` executes immediately and returns whether the button was clicked during that frame. The library stores only transient identifiers using an internal ID stack (`ImGui::PushID`) and minimal caches for navigation state. The source of truth for all application data lives in your code, not in the UI library.

### Retained-Mode Philosophy

Traditional toolkits like Qt, WinForms, or WPF operate on a retained-mode architecture. These frameworks instantiate persistent widget objects that form a scene graph, owning their own state, properties, and lifecycle. The toolkit manages layout passes, dirty region updates, and event dispatching through signal/slot connections or callback mechanisms. When the application needs to change the UI, it must modify these retained objects and trigger invalidation and re-layout passes.

## State Management and Performance

**State Ownership** represents the most significant divergence between the two paradigms. Dear ImGui requires the application to supply all state every frame—for example, passing the current value to `ImGui::SliderFloat("Value", &myValue, 0.0f, 1.0f)`. In contrast, retained-mode sliders store their current position internally, requiring the application to query or synchronize with the widget's state.

**Rendering Performance** differs substantially because Dear ImGui eliminates scene graph traversal. During each frame, widget calls append geometry to `ImDrawList` objects (implemented in [`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_draw.cpp)). After `ImGui::Render()` completes, the host application receives a flat `ImDrawData` structure containing vertex buffers and draw commands. There are no layout recomputations or off-screen buffers—just a straight list of draw calls to feed to the GPU.

Retained-mode toolkits must traverse the widget hierarchy to compute layouts, identify dirty regions, and batch painting operations. This overhead grows with UI complexity and can introduce frame drops during complex layout passes.

## Code Comparison: Immediate vs Retained

The following examples illustrate the architectural difference when implementing a simple button and slider interface.

### Dear ImGui (Immediate-Mode)

```cpp
// Inside your render loop
ImGui::Begin("Settings");
if (ImGui::Button("Click Me")) {
    // Immediate execution path
    printf("Button pressed this frame!\n");
}
ImGui::SliderFloat("Volume", &volume, 0.0f, 1.0f);
ImGui::End();

```

All UI logic executes linearly without callbacks. The `volume` variable lives in your application code; ImGui merely reads and writes to it during the frame.

### Qt (Retained-Mode)

```cpp
// Setup phase - widgets persist throughout application lifetime
QPushButton *button = new QPushButton("Click Me", parent);
connect(button, &QPushButton::clicked, [](){
    qDebug() << "Button clicked via signal/slot";
});

QSlider *slider = new QSlider(Qt::Horizontal, parent);
slider->setRange(0, 100);
slider->setValue(static_cast<int>(volume * 100));

```

Here, widgets are heap-allocated objects that endure between frames. Interaction occurs through the Qt signal/slot mechanism, decoupling the trigger from the handler.

## Integration and Rendering Pipeline

Dear ImGui is designed for embedding into existing render loops rather than owning the windowing system. The host application populates the `ImGuiIO` structure with input data (mouse coordinates, keyboard states, delta time) as shown in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) around line 6628. ImGui processes these inputs immediately, updating navigation and interaction states without requiring OS-level event loops.

The rendering abstraction is minimal. After the frame concludes, the backend (such as [`backends/imgui_impl_opengl3.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_opengl3.cpp)) iterates over `ImDrawData` and submits vertex buffers to the GPU. This makes integration with OpenGL, Vulkan, DirectX, or custom engines straightforward—you retain full control over the render pipeline.

Retained-mode frameworks typically require ownership of the message loop, window creation, and often enforce specific threading models. Integration into existing game engines or real-time visualization tools requires significant boilerplate or "widget host" abstractions.

## When to Choose Each Approach

**Choose Dear ImGui when:**
- Building **debugging tools**, profilers, or parameter tweak panels that require rapid iteration
- Developing **game engines** or real-time applications where you already control the render loop and need low-overhead overlays
- **Prototyping** interfaces where the structure changes frequently, as you can modify layout logic without worrying about stale widget references or lifetime management

**Choose Retained-Mode when:**
- Building **complex data-driven applications** like document editors or IDEs requiring advanced accessibility, sophisticated theming, or platform-native look-and-feel
- Working with **declarative frameworks** where UI description should remain separate from imperative logic (similar to React or XAML patterns)
- Team structure requires **UI/UX specialists** to modify interface layouts without touching C++ application logic

## Summary

- **Dear ImGui uses immediate-mode**: UI is rebuilt every frame with no persistent widget objects, placing state ownership in application code
- **Retained-mode uses persistent trees**: Widgets own their state and exist as objects between frames, requiring callbacks for interaction
- **Performance characteristics**: Dear ImGui generates flat draw lists (`ImDrawData`) without scene graph traversal; retained-mode requires layout passes and dirty region calculations
- **Integration model**: Dear ImGui consumes `ImGuiIO` inputs and emits vertex buffers, making it ideal for embedding in existing render loops; retained-mode typically owns the windowing system
- **Debugging advantage**: Dear ImGui allows standard breakpoints and print debugging since UI flow is linear code; retained-mode debugging often requires inspecting opaque widget trees

## Frequently Asked Questions

### What is the ImGui ID stack and why does it matter?

The ID stack is a deterministic hashing system (accessed via `ImGui::PushID` and `ImGui::PopID`) that allows Dear ImGui to uniquely identify widgets across frames without storing persistent objects. This enables the library to track which widget is active, hovered, or focused while remaining stateless. According to [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp), this system allows ImGui to retain minimal internal caches for navigation while keeping the API immediate.

### Can Dear ImGui handle complex layouts like retained-mode toolkits?

Yes, but with different trade-offs. Dear ImGui supports sophisticated layouts through functions like `ImGui::SameLine()`, `ImGui::BeginTable()`, and the `ImGui::SetNextWindowPos` API. However, because the layout is recomputed every frame, you avoid the "stale layout" problems common in retained-mode when widgets resize dynamically. The [`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp) file contains extensive examples of complex layouts implemented entirely in immediate-mode.

### How does input handling work without events?

Instead of an event queue, Dear ImGui samples input state via the `ImGuiIO` structure populated by the host before `ImGui::NewFrame()`. The library processes mouse positions, button states, and keyboard inputs immediately during widget calls. As implemented in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) around line 6628, this immediate input processing allows the UI to react without latency introduced by event loop dispatching or signal/slot overhead.

### Is immediate-mode suitable for end-user applications or just debugging tools?

While traditionally favored for debug UIs and game tools, Dear ImGui powers many shipping applications including games and creative tools. The library is suitable for end-user interfaces when you need maximum performance and don't require native OS theming. However, for applications requiring screen readers, platform-native widgets, or complex document editing, retained-mode toolkits remain preferable due to their mature accessibility implementations and persistent object model.