ImGui Immediate Mode GUI vs Retained Mode: Architecture and Performance Differences

Dear ImGui implements an immediate mode GUI paradigm where the interface is rebuilt from scratch every frame via function calls that both describe layout and handle input, eliminating the persistent widget objects required by retained mode systems.

Dear ImGui (ocornut/imgui) follows an immediate mode GUI design that fundamentally reimagines how user interfaces are constructed and rendered. Unlike traditional GUI frameworks that rely on retained mode object hierarchies, ImGui processes UI elements statelessly each frame. This architecture moves state ownership from the UI library to the application, resulting in a simpler API and more efficient batch rendering while avoiding the complexity of maintaining persistent widget trees.

What Defines Immediate Mode GUI in Dear ImGui

The immediate mode paradigm in Dear ImGui centers on the principle that UI code is executed procedurally every frame rather than modifying long-lived object structures. This approach is implemented throughout the codebase, from the public API in imgui.h to the core logic in imgui.cpp.

Stateless Widget Processing

In the immediate mode model, function calls such as ImGui::Button("OK") do not instantiate persistent widget objects. Instead, as implemented in the internal UI state machine, each call queries the current input state, determines whether the widget is hovered or clicked, updates minimal internal navigation state, and immediately returns a boolean result (e.g., true when the button is pressed). This stateless processing eliminates the need for the library to maintain a parallel object hierarchy of buttons, sliders, and windows.

Single Frame Data Lifecycle

All UI data in Dear ImGui exists only for the duration of the frame. Persistent state that applications require—such as the current value of a slider or the contents of a text field—must be stored in application-side variables. The library reads from and writes to these external variables during the frame execution, as seen in functions like ImGui::SliderFloat("Value", &value, 0.0f, 1.0f), which directly modifies the application's value variable.

Implicit UI Hierarchy

Unlike retained mode systems that build explicit tree structures of parent and child widgets, Dear ImGui defines the UI hierarchy implicitly through the order of function calls within each frame. The ImGui::Begin() and ImGui::End() calls create a stack-based scope that organizes layout without allocating persistent container objects, allowing the visual structure to change dynamically every frame based on application logic.

Critical Differences: Immediate Mode vs Retained Mode

The architectural gap between these paradigms affects everything from memory management to event handling. The following comparison breaks down how Dear ImGui's approach differs from traditional retained mode frameworks:

Aspect Immediate Mode (Dear ImGui) Retained Mode
Widget Lifecycle Created, processed, and destroyed each frame; no long-lived objects exist between frames. Widgets are instantiated once and persist until explicitly destroyed by the application.
Event Handling Events are polled directly inside the widget function call; the function returns interaction flags immediately. Events are dispatched to existing widget objects via callbacks, listeners, or signal-slot mechanisms.
State Storage Application owns all state; ImGui maintains only minimal internal state for navigation and focus management. Widget objects encapsulate and own their own state (text content, selection ranges, scroll positions).
Performance Model UI description is computationally cheap; geometry is batched into vertex buffers and emitted as a small number of draw commands. The retained engine may batch internally but often requires traversal of a persistent scene graph each frame.
API Complexity Simpler inline API where UI code is written directly where needed, with immediate feedback values. More boilerplate required to maintain separate widget objects, set up data bindings, and manage lifecycles.

How Dear ImGui Optimizes Rendering Without Immediate Mode GPU Drawing

Despite rebuilding the UI logic every frame, Dear ImGui does not issue a draw call per widget. According to the docs/README.md in the repository, the library avoids the "hammering the GPU" pitfall often associated with naive immediate mode rendering.

Instead, the implementation in imgui_draw.cpp records vertices into internal buffers and emits a compact list of draw commands. These commands are generated during ImGui::Render() and consumed by back-end implementations such as backends/imgui_impl_opengl3.cpp, which translates the draw data into efficient GPU operations. This batching mechanism allows the immediate mode API to remain simple while achieving rendering performance comparable to or better than retained mode systems.

Practical Implementation: Code Examples

The following examples demonstrate the immediate mode workflow in a typical application loop. Notice how the UI is constructed procedurally each frame without persisting widget objects.

// Simple window with button and slider
bool show_demo = true;
float value = 0.0f;

ImGui::Begin("Demo Window", &show_demo);           // Re-creates window each frame
if (ImGui::Button("Press Me")) {                   // Returns true only on click frame
    value = 0.0f;                                  // Immediate response to input
}
ImGui::SliderFloat("Value", &value, 0.0f, 1.0f);   // Direct memory read/write
ImGui::End();                                      // Layout finalized for this frame
// Complete frame loop showing immediate mode integration
while (running) {
    // 1. Prepare new frame
    ImGui_ImplXXX_NewFrame();
    ImGui::NewFrame();

    // 2. Build UI (immediate mode pass)
    ShowDemoWindow(&show_demo);

    // 3. Generate draw lists and render
    ImGui::Render();
    ImGui_ImplXXX_RenderDrawData(ImGui::GetDrawData());

    // 4. Swap buffers
}

Key Source Files in the ImGui Repository

Understanding the immediate mode implementation requires familiarity with these core files in the ocornut/imgui repository:

  • imgui.h — Public API exposing immediate mode functions (ImGui::Begin, ImGui::Button, ImGui::SliderFloat, etc.)
  • imgui.cpp — Core implementation of the UI state machine, input handling, and frame logic
  • imgui_draw.cpp — Vertex buffer construction, draw-list generation, and geometry batching
  • backends/imgui_impl_opengl3.cpp — Example renderer back-end consuming ImGui's draw data
  • docs/README.md — Documentation clarifying immediate mode concepts and common misconceptions

Summary

  • Dear ImGui uses an immediate mode GUI approach where widgets are recreated every frame through direct function calls rather than persistent objects.
  • State ownership remains with the application, requiring external variables for values that persist across frames.
  • Event handling is synchronous and polled within widget functions, eliminating callback systems.
  • Despite the per-frame rebuilding, rendering is optimized through draw-list batching in imgui_draw.cpp, not individual GPU draw calls per widget.
  • This architecture reduces API complexity and boilerplate while maintaining high performance for tools and debug interfaces.

Frequently Asked Questions

Does immediate mode mean Dear ImGui draws every widget individually to the GPU?

No. While the UI logic runs every frame, the actual graphics processing is optimized through batching. As implemented in imgui_draw.cpp, the library accumulates vertices into buffers and emits a small set of draw commands. The back-end renderer (e.g., imgui_impl_opengl3.cpp) then processes these efficiently, avoiding the performance cost of individual draw calls per widget.

Where is UI state stored if there are no persistent widget objects?

All persistent state lives in application variables. When calling ImGui::SliderFloat or similar functions, you pass pointers to your own variables. Dear ImGui only maintains minimal transient state for navigation and focus, while the application owns the actual data (floats, strings, booleans) that widgets display and modify.

How does immediate mode event handling differ from retained mode callbacks?

In Dear ImGui's immediate mode, event handling happens synchronously within the widget function call. ImGui::Button returns a boolean immediately indicating whether it was clicked this frame, allowing you to handle the event inline with if (ImGui::Button("OK")) { /* handle */ }. Retained mode systems use asynchronous callbacks where widgets live between frames and notify listeners when state changes occur.

Can Dear ImGui be used alongside retained mode UI systems?

Yes. Dear ImGui operates independently of your application's object model, making it suitable for overlaying debug tools or editor interfaces on top of retained mode game engines or applications. Since ImGui does not require ownership of application state or objects, it can coexist with existing widget hierarchies without interference, provided you manage the render order and input processing appropriately.

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 →