Creating Popup Modals and Context Menus in Dear ImGui: A Complete Guide

Dear ImGui implements popups, modals, and context menus as temporary overlay windows managed by an internal popup stack, using a three-step pattern of triggering with OpenPopup(), beginning with BeginPopup() or BeginPopupModal(), and closing automatically or via CloseCurrentPopup().

When building immediate-mode interfaces with the ocornut/imgui repository, creating popup modals and context menus in ImGui follows a consistent stack-based architecture that differs from traditional window management. Unlike persistent window objects, these elements live only for the duration of the frame loop as temporary overlays, making them lightweight, naturally nestable, and simple to implement.

The Three-Step Popup Lifecycle

Every popup in Dear ImGui follows an identical workflow regardless of whether it is a simple tooltip, a blocking modal dialog, or a right-click context menu. This pattern relies on an internal g.OpenPopupStack that tracks which overlays are currently active.

Step 1: Triggering the Popup

To display a popup, you must first signal Dear ImGui to open it by calling ImGui::OpenPopup() or a helper variant such as ImGui::OpenPopupOnItemClick(). This function pushes an ImGuiPopupData entry onto the global g.OpenPopupStack, storing the popup's identifier, opening position, and frame count. According to the source in imgui.cpp around line 12413, OpenPopupEx performs this stack manipulation.

if (ImGui::Button("Show Options"))
    ImGui::OpenPopup("MyPopup");  // Stores ID in g.OpenPopupStack

Step 2: Beginning the Popup Window

Inside your frame loop, call ImGui::BeginPopup() for standard overlays or ImGui::BeginPopupModal() for dialogs that block interaction with the rest of the UI. These functions check g.OpenPopupStack against the current BeginPopupStack and return true only if the popup was actually triggered this frame, allowing you to render contents safely inside the conditional block. The core logic resides in imgui.cpp near line 12653, where BeginPopupEx validates the stack and creates a temporary window with ImGuiWindowFlags_Popup.

if (ImGui::BeginPopup("MyPopup"))
{
    // Contents only render when popup is active
    ImGui::EndPopup();
}

Step 3: Rendering Content and Handling Closure

Once inside the popup block, add any widgets needed. The overlay closes automatically when the user clicks outside, selects a menu item, or you explicitly call ImGui::CloseCurrentPopup(), which removes the top entry from the stack as implemented in imgui.cpp near line 12575. For modal dialogs, you must also manage a boolean state variable to prevent immediate reopening.

Core Implementation Details

The robustness of Dear ImGui's popup system comes from its centralized stack management in imgui.cpp. Key functions include:

  • OpenPopupEx (imgui.cpp#L12413‑L12430): Validates the popup ID and appends it to g.OpenPopupStack, handling positioning and focus.
  • BeginPopupEx (imgui.cpp#L12653‑L12663): Compares the requested ID against the open stack and initializes the window with ImGuiWindowFlags_Popup or ImGuiWindowFlags_Modal.
  • CloseCurrentPopup (imgui.cpp#L12575‑L12592): Pops the topmost entry from the stack, effectively closing the current overlay.

Because popups use string identifiers (or ImGuiID) relative to the current ID stack, you can maintain several independent popups simultaneously without naming collisions, and nesting works naturally when a popup trigger is placed inside another popup's block.

Practical Code Examples

Simple Popup Window (Non-Modal)

This example demonstrates a basic popup triggered by a button, referencing the "Color Picker" implementation in imgui_demo.cpp around line 1255.

if (ImGui::Button("Show Popup"))
    ImGui::OpenPopup("MySimplePopup");

if (ImGui::BeginPopup("MySimplePopup"))
{
    ImGui::Text("This is a regular popup.");
    if (ImGui::Button("Close"))
        ImGui::CloseCurrentPopup();  // Optional: clicking elsewhere also closes
    ImGui::EndPopup();
}

Modal windows block background interaction until dismissed. This pattern appears in the "Delete?" confirmation dialog in imgui_demo.cpp near line 5554.

static bool show_modal = false;
if (ImGui::Button("Delete…")) 
    show_modal = true;

if (show_modal)
    ImGui::OpenPopup("DeleteConfirm");

if (ImGui::BeginPopupModal("DeleteConfirm", nullptr,
                           ImGuiWindowFlags_AlwaysAutoResize))
{
    ImGui::Text("Are you sure you want to delete the item?");
    if (ImGui::Button("Yes"))
    {
        // …perform deletion…
        show_modal = false;
        ImGui::CloseCurrentPopup();
    }
    ImGui::SameLine();
    if (ImGui::Button("Cancel"))
    {
        show_modal = false;
        ImGui::CloseCurrentPopup();
    }
    ImGui::EndPopup();
}

Context Menu on Items

Context menus use helper functions from imgui_widgets.cpp to automatically handle right-click detection. The following pattern appears throughout imgui_demo.cpp near lines 2360 and 5466.

ImGui::Text("Right‑click me");
if (ImGui::BeginPopupContextItem())   // Uses last item's ID as popup identifier
{
    if (ImGui::MenuItem("Copy"))
        /* …copy logic… */;
    if (ImGui::MenuItem("Delete"))
        /* …delete logic… */;
    ImGui::EndPopup();
}

For window-level context menus (triggered by right-clicking the background), use ImGui::BeginPopupContextWindow() instead.

Summary

  • Popups are stack-based: Dear ImGui uses g.OpenPopupStack to manage overlay state, with OpenPopup() to push and CloseCurrentPopup() to pop.
  • Three functions define the lifecycle: Trigger with OpenPopup(), begin rendering with BeginPopup() or BeginPopupModal(), and close implicitly or explicitly.
  • Identifiers are hierarchical: Popup IDs respect the current ID stack, enabling natural nesting and scoping without global name pollution.
  • Reference implementations: Study imgui_demo.cpp for usage patterns, imgui_widgets.cpp for context menu helpers, and imgui.cpp for the core stack logic.

Frequently Asked Questions

What is the difference between BeginPopup and BeginPopupModal?

BeginPopup creates a standard overlay that allows interaction with the rest of the UI and closes automatically when clicking outside. BeginPopupModal creates a blocking dialog that disables interaction with background windows and requires explicit closure via CloseCurrentPopup() or programmatic state management, as shown in the delete confirmation example in imgui_demo.cpp#L5554‑L5608.

Can I nest popups inside other popups in ImGui?

Yes. Because popup identifiers are resolved relative to the current ID stack, you can call OpenPopup() and BeginPopup() from within another popup's rendering block. Dear ImGui maintains separate entries in g.OpenPopupStack for each level, allowing unlimited nesting depth provided each popup has a unique identifier within its parent's scope.

How do I prevent a popup from closing when the user clicks outside?

Use ImGui::BeginPopupModal() instead of ImGui::BeginPopup(). Modal windows explicitly block background clicks from closing the overlay and remain open until you call CloseCurrentPopup() or manually clear the trigger state. Standard popups created with BeginPopup() always close on outside clicks by design.

What is the difference between BeginPopupContextItem and BeginPopupContextWindow?

BeginPopupContextItem() opens a context menu when right-clicking the last drawn item, automatically using that item's ID as the popup identifier. BeginPopupContextWindow() opens a menu when right-clicking anywhere in the current window's background or empty space. Both helpers are implemented in imgui_widgets.cpp and handle the OpenPopup() call internally.

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 →