How to Create Modal Dialogs and Popup Windows in ImGui

Modal dialogs in ImGui block all background interaction and require BeginPopupModal(), while standard popups use BeginPopup() and allow clicking outside to dismiss.

Creating user interface overlays in the ocornut/imgui library requires understanding the distinction between blocking modal dialogs and transient popup windows. While both constructs share the same underlying window system in imgui.cpp, they differ significantly in their API entry points, input handling, and dismissal behavior. This guide explains how to correctly implement each pattern using the exact function signatures defined in imgui.h.

Dear ImGui distinguishes between two primary overlay types based on their interaction model and rendering flags.

Modal Dialogs are blocking windows that capture all input and automatically dim the background. They must be dismissed programmatically or via explicit user action before the user can interact with other UI elements.

Regular Popups are non-blocking overlays typically attached to a parent item (such as a right-click context menu). They do not prevent interaction with other windows and automatically close when the user clicks outside their bounds or presses Esc.

The key differences are summarized in the implementation details:

  • Blocking Behavior: Modals prevent all other UI interaction until CloseCurrentPopup() is called or the open flag is cleared. Popups only block interaction with their own contents.
  • Background Dimming: Modals automatically darken the screen behind them via the internal ImGuiWindowFlags_Modal flag (defined at line 1199 in imgui.h). Popups leave the background fully interactive and undimmed.
  • Close Mechanisms: Modals require explicit closing logic in your code. Popups support implicit closing through outside clicks or the Esc key.

Core API Functions and Flags

The public API in imgui.h exposes specific entry points for each dialog type.

BeginPopupModal

To create a modal dialog, call BeginPopupModal() with a unique identifier string. As declared at line 849 of imgui.h:

IMGUI_API bool BeginPopupModal(const char* name,
                               bool* p_open = NULL,
                               ImGuiWindowFlags flags = 0);

This function returns true only when the modal is actually open, allowing you to emit its contents. The optional p_open pointer enables automatic handling of the title bar close button; ImGui will set this boolean to false if the user clicks "X".

Important: Do not manually set the ImGuiWindowFlags_Modal flag. This bit is reserved for internal use (as noted at line 1199) and is applied automatically by BeginPopupModal().

BeginPopup and Context Wrappers

For non-blocking popups, use BeginPopup() or convenience wrappers like BeginPopupContextItem() and BeginPopupContextWindow(). These functions also return true when the popup is active:

if (ImGui::BeginPopup("MyContext")) {
    // Render popup contents
    ImGui::EndPopup();
}

Closing Popups

Regardless of type, always end the popup scope with EndPopup(). To close the popup from within its body, call CloseCurrentPopup(). The modal or popup is automatically removed from the internal stack on the next frame.

Step-by-Step Implementation Guide

Opening a Modal Dialog

Unlike regular windows, modals require an explicit open request. The standard pattern uses a state boolean to trigger the modal on the next frame:

  1. Set a boolean flag to true when the user triggers the action (e.g., clicking a delete button).
  2. In your main UI loop, check this flag and call BeginPopupModal().
  3. Render the contents inside the if block.
  4. Call EndPopup() to close the scope.

This deferred opening ensures ImGui correctly manages the window focus stack in imgui.cpp.

Handling the Close Button and State Management

When you pass a boolean pointer (p_open) to BeginPopupModal(), ImGui automatically checks for title bar close clicks. However, you must preserve this state after the call:

bool keep_open = true;
if (ImGui::BeginPopupModal("Settings", &keep_open)) {
    // ... contents ...
    ImGui::EndPopup();
}
if (!keep_open) {
    // Handle closure logic here
}

If you do not provide the p_open pointer, you must explicitly call CloseCurrentPopup() or rely on programmatic logic to stop calling BeginPopupModal() in subsequent frames.

Creating Regular Context Menu Popups

For transient menus, use OpenPopup() to push the ID to the stack, then test BeginPopup():

if (ImGui::Button("Options")) {
    ImGui::OpenPopup("ContextMenu");
}
if (ImGui::BeginPopup("ContextMenu")) {
    if (ImGui::MenuItem("Copy")) { /* ... */ }
    ImGui::EndPopup();
}

This pattern does not block the background and automatically handles dismissal when the user clicks elsewhere.

Practical Code Examples

Simple Confirmation Modal

This example demonstrates a blocking confirmation dialog with explicit state management. The modal opens when show_confirm is true and closes via CloseCurrentPopup():

static bool show_confirm = false;
static bool confirm_result = false;

if (ImGui::Button("Delete File")) {
    show_confirm = true;
}

if (show_confirm && ImGui::BeginPopupModal("Confirm Delete", nullptr,
                                           ImGuiWindowFlags_AlwaysAutoResize)) {
    ImGui::TextWrapped("Are you sure you want to delete this file?");
    ImGui::Separator();
    
    if (ImGui::Button("Yes", ImVec2(120, 0))) {
        confirm_result = true;
        show_confirm = false;
        ImGui::CloseCurrentPopup();
    }
    ImGui::SameLine();
    if (ImGui::Button("Cancel", ImVec2(120, 0))) {
        confirm_result = false;
        show_confirm = false;
        ImGui::CloseCurrentPopup();
    }
    ImGui::EndPopup();
}

Passing the state pointer allows ImGui to handle the "X" button automatically. Synchronize the flag after the call to capture title bar closures:

static bool open_settings = false;
if (ImGui::MenuItem("Settings")) {
    open_settings = true;
}

bool keep_open = open_settings;
if (ImGui::BeginPopupModal("Settings", &keep_open,
                           ImGuiWindowFlags_NoResize)) {
    ImGui::Text("Adjust application options here.");
    
    if (ImGui::Button("Save")) {
        keep_open = false;
    }
    ImGui::EndPopup();
}
open_settings = keep_open;  // Update state if user clicked title bar X

Regular Context Popup

This non-blocking popup attaches to a button and allows background interaction:

if (ImGui::Button("Right-Click Me")) {
    ImGui::OpenPopup("MyContext");
}

if (ImGui::BeginPopup("MyContext")) {
    if (ImGui::MenuItem("Option A")) { /* handle A */ }
    if (ImGui::MenuItem("Option B")) { /* handle B */ }
    ImGui::EndPopup();
}

Summary

  • Use BeginPopupModal() for blocking dialogs that must capture all input and dim the background, as implemented in imgui.cpp.
  • Pass p_open to BeginPopupModal() to enable the title bar close button, but remember to synchronize the boolean after the call.
  • Use BeginPopup() for non-blocking context menus that should close when clicking outside.
  • Always call EndPopup() to close the scope, and use CloseCurrentPopup() to dismiss the popup programmatically.
  • Never manually set ImGuiWindowFlags_Modal (line 1199 of imgui.h); this flag is internal to the library.

Frequently Asked Questions

What is the difference between BeginPopup() and BeginPopupModal()?

BeginPopup() creates a transient overlay that does not block other windows and closes automatically when clicking outside or pressing Esc. BeginPopupModal() creates a blocking dialog that dims the background and prevents interaction with other UI elements until explicitly dismissed via CloseCurrentPopup() or a cleared boolean flag.

How do I close a modal dialog when the user clicks a button?

Call ImGui::CloseCurrentPopup() inside the modal's body. If you passed a bool* p_open to BeginPopupModal(), you can also set *p_open = false. The modal will be removed from the internal stack on the next frame after EndPopup() is called.

Why does my modal not open when I call BeginPopupModal()?

BeginPopupModal() returns false unless the popup is actually open in the current frame. You must either call ImGui::OpenPopup() before testing BeginPopupModal(), or use a persistent boolean flag to trigger the modal on the next frame. This deferred opening mechanism allows ImGui to manage focus and z-order correctly in the internal window system.

Can I manually add the ImGuiWindowFlags_Modal flag to a regular window?

No. According to the source code at line 1199 of imgui.h, ImGuiWindowFlags_Modal is marked for internal use only. The flag is automatically applied by BeginPopupModal(). Manually setting it on a regular window created with Begin() will result in undefined behavior or failed assertions in debug builds.

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 →