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

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 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). 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)

// 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)

// 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 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) 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, 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 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →