Understanding the Immediate Mode GUI Paradigm in Dear ImGui

Dear ImGui implements an immediate-mode GUI paradigm where widgets are recreated and processed every frame through direct function calls that immediately return interaction results, eliminating the need for persistent widget objects or asynchronous event callbacks.

Dear ImGui (ocornut/imgui) is built around the immediate mode GUI design philosophy, a fundamentally different approach to user interface programming compared to traditional retained-mode toolkits. In this paradigm, the entire UI is reconstructed from scratch each frame by executing a sequence of function calls that both describe the layout and handle user input in a single pass. This architecture eliminates persistent widget objects and moves state management responsibility directly to the application code.

Core Principles of Immediate Mode GUI

Stateless Widget Rendering

Each call such as ImGui::Button("OK") does not instantiate a persistent object. Instead, as implemented in imgui.cpp, the function queries the current input state, determines hover and click status, updates minimal internal navigation state, and immediately returns a boolean result. There are no long-lived button objects stored in memory between frames; the widget exists only for the duration of the function call.

Single-Frame Data Lifecycle

All UI data in Dear ImGui exists only for the duration of the frame. Persistent state—such as values edited by sliders or text input buffers—must be stored in application-side variables and passed by reference to functions like ImGui::SliderFloat(). The library maintains only transient internal state for navigation, focus, and keyboard handling, while the application owns all data that must persist across frames.

Immediate Mode vs Retained Mode: Architectural Differences

Widget Lifecycle

  • Immediate Mode (Dear ImGui): Widgets are created, processed, and destroyed each frame. The UI hierarchy is implicitly defined by the order of function calls in imgui.h, not by a persistent object tree that lives between frames.
  • Retained Mode: Widgets are instantiated once as objects that persist across frames until explicitly destroyed, requiring maintenance of a separate scene graph or widget hierarchy.

Event Handling

  • Immediate Mode: Events are polled directly inside the widget call. ImGui::Button() returns true immediately when clicked during that frame, allowing the application to handle logic inline without registering callbacks.
  • Retained Mode: Events are dispatched asynchronously to existing widget objects through callbacks, listeners, or signal-slot mechanisms, requiring separate event handling infrastructure.

State Storage

  • Immediate Mode: The application owns all persistent state. Dear ImGui keeps only minimal internal state (e.g., navigation history, focus) in imgui.cpp, while values live in your variables that you pass by pointer.
  • Retained Mode: Widget objects encapsulate and own their own state, such as text buffers, selection ranges, and scroll positions, requiring synchronization between application data and UI objects.

Performance Characteristics

  • Immediate Mode: UI description is computationally cheap. Despite rebuilding the UI each frame, Dear ImGui batches geometry into vertex buffers in imgui_draw.cpp, emitting only a small number of draw commands to the back-end. It does not issue a GPU draw call per widget, contrary to common misconceptions addressed in docs/README.md.
  • Retained Mode: Engines may batch internally but often require full scene graph traversal and synchronization between application state and widget objects, adding memory and processing overhead.

Source Code Architecture

The immediate-mode API is exposed in imgui.h, which declares functions like ImGui::Begin(), ImGui::Button(), and ImGui::SliderFloat(). The core implementation in imgui.cpp manages the UI state machine, input handling, and draw-list generation. Actual vertex buffer construction and batching occurs in imgui_draw.cpp, ensuring that the immediate-mode API doesn't translate to immediate-mode GPU rendering. Example back-end implementations like backends/imgui_impl_opengl3.cpp consume this batched draw data for final rendering to the screen.

Code Example: Immediate Mode Implementation

// Application variables own the state
bool show_demo = true;
float value = 0.0f;

// Rebuild UI every frame
ImGui::Begin("Demo Window", &show_demo);           // Defined in imgui.h
if (ImGui::Button("Press Me")) {                  // Returns true on click this frame
    value = 0.0f;                                // Handle immediately
}
ImGui::SliderFloat("Value", &value, 0.0f, 1.0f); // Read/write external variable
ImGui::End();                                      // Window layout finalized

In your main loop:

while (running) {
    // 1. Start frame
    ImGui_ImplXXX_NewFrame();
    ImGui::NewFrame();

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

    // 3. Render: ImGui builds draw lists, back-end draws them
    ImGui::Render();
    ImGui_ImplXXX_RenderDrawData(ImGui::GetDrawData());
}

Summary

  • Dear ImGui uses an immediate-mode GUI paradigm where the UI is rebuilt from scratch every frame through direct function calls.
  • No persistent widgets: Functions like ImGui::Button() execute statelessly and return results immediately, without creating objects that live between frames.
  • Application owns state: Persistent data must be stored in application variables and passed by reference to ImGui functions.
  • Efficient rendering: Despite the immediate-mode API, the implementation in imgui_draw.cpp batches geometry into vertex buffers, avoiding GPU draw call overhead.
  • Simpler code: The paradigm eliminates boilerplate for widget creation, event callbacks, and state synchronization found in retained-mode systems.

Frequently Asked Questions

Does immediate mode mean Dear ImGui issues a GPU draw call for every widget?

No. While the API follows immediate-mode semantics, the rendering is heavily batched. Dear ImGui accumulates vertices into buffers in imgui_draw.cpp and emits a minimal set of draw commands. The back-end renderer (e.g., imgui_impl_opengl3.cpp) processes these batched commands, avoiding the performance penalty of individual draw calls per widget. This distinction is clarified in the repository's docs/README.md.

Where is widget state stored if ImGui doesn't retain objects?

Widget state is stored in your application. For example, ImGui::SliderFloat() takes a pointer to a float variable that you own. Dear ImGui maintains only minimal internal state for navigation, focus, and keyboard input handling in imgui.cpp. This design forces explicit state management and prevents hidden synchronization bugs between the UI and application logic.

How does event handling work without callbacks?

Events are polled synchronously during the widget function call. When you call ImGui::Button("OK"), it returns true immediately if the button was clicked during that frame, allowing you to handle the logic inline. This eliminates the need for callback registration, signal-slot systems, or event listeners required by retained-mode frameworks.

Is the immediate-mode GUI paradigm suitable for complex applications?

Yes. Dear ImGui's approach scales to complex tools and game engines because the cost of describing the UI is negligible compared to the actual rendering work. The library handles complexity through efficient draw-list batching in imgui_draw.cpp and provides utilities for layout, docking, and multi-window management while maintaining the simple, stateless API surface defined in imgui.h.

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 →