# Creating Popup Modals and Context Menus Correctly in Dear ImGui

> Learn to correctly create popup modals and context menus in Dear ImGui using matched OpenPopup and BeginPopup calls. Essential for managing UI elements seamlessly.

- Repository: [omar/imgui](https://github.com/ocornut/imgui)
- Tags: how-to-guide
- Published: 2026-07-14

---

**Dear ImGui implements popups as a self-contained stack inside the global `ImGuiContext`, requiring matched `OpenPopup` and `BeginPopup` calls at the same ID stack depth to display modal windows and context menus correctly.**

Dear ImGui (ocornut/imgui) provides a flexible popup system for creating modal dialogs and right-click context menus, but correct usage depends on understanding the internal ID stack and the two-phase open-begin lifecycle. This guide explains how to create popup modals and context menus correctly in Dear ImGui by referencing the actual source implementation in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) and [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) and avoiding common pitfalls that break the popup state machine.

## How Dear ImGui Manages Popup State

Popups in Dear ImGui are not standalone windows but entries in a per-frame stack stored in `g.BeginPopupStack` (declared at **[imgui_internal.h#L2373](https://github.com/ocornut/imgui/blob/master/imgui_internal.h#L2373)**). When you open a popup, the library creates an `ImGuiPopupData` entry that persists only while the popup remains active. The identifier for each popup is resolved against the current **ID stack**, which is why `OpenPopup` and `BeginPopup` must be called at the exact same nesting level—mismatched depth causes the lookup to fail silently.

## The Four-Step Popup Lifecycle

All popups follow a strict lifecycle defined by four core functions from **[imgui.h](https://github.com/ocornut/imgui/blob/master/imgui.h)**:

1. **`OpenPopup`**: Marks the popup as open by pushing its ID onto the internal stack. This must be called **once per triggering event**, not every frame. Available overloads include `OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0)` at **[imgui.h#L59](https://github.com/ocornut/imgui/blob/master/imgui.h#L59)** and an `ImGuiID` variant for manual ID handling.

2. **`BeginPopup` or `BeginPopupModal`**: Queries the stored state. If the popup is open, it creates an `ImGuiWindow` and returns `true` so you can populate contents. `BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0)` is at **[imgui.h#L848](https://github.com/ocornut/imgui/blob/master/imgui.h#L848)**, while `BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0)` is at **[imgui.h#L849](https://github.com/ocornut/imgui/blob/master/imgui.h#L849)**. The modal variant automatically sets `ImGuiWindowFlags_Modal` (defined at **[imgui.h#L1198-L1199](https://github.com/ocornut/imgui/blob/master/imgui.h#L1198)**), blocking interaction with background windows and adding a dimmed overlay.

3. **`EndPopup`**: Closes the window created by `BeginPopup*`. Must be called **only if** the corresponding begin function returned `true`, located at **[imgui.h#L850](https://github.com/ocornut/imgui/blob/master/imgui.h#L850)**.

4. **`CloseCurrentPopup`** (optional): Forces the current popup to close from within its own code block, defined at **[imgui.h#L62](https://github.com/ocornut/imgui/blob/master/imgui.h#L62)**.

## Context Menu Helpers

For right-click context menus, Dear ImGui provides combined helpers that handle both the open trigger and the begin call automatically:

- **`BeginPopupContextItem`**: Opens when right-clicking the last item. Signature at **[imgui.h#L873](https://github.com/ocornut/imgui/blob/master/imgui.h#L873)**.
- **`BeginPopupContextWindow`**: Opens when right-clicking anywhere in the current window. Signature at **[imgui.h#L874](https://github.com/ocornut/imgui/blob/master/imgui.h#L874)**.
- **`BeginPopupContextVoid`**: Opens when right-clicking the background void (no window). Signature at **[imgui.h#L875](https://github.com/ocornut/imgui/blob/master/imgui.h#L875)**.

These functions internally call `OpenPopupOnItemClick` using the right mouse button by default, then immediately call `BeginPopup`. Pass `ImGuiPopupFlags_NoOpenOverExistingPopup` to `popup_flags` to prevent overlapping menus.

## The ID Stack Discipline

The most common source of popup failures is violating the **ID stack** rule. Because popup IDs are hashed relative to the current ID stack, calling `OpenPopup` inside a tree node (`TreeNode`) or group (`BeginGroup`) means you must call the matching `BeginPopup` inside that same node or group. If the nesting levels differ, the hashed IDs will not match and the popup will never appear. Always ensure `OpenPopup` and `BeginPopup` occur at the same scope level.

## Common Pitfalls to Avoid

- **Calling `OpenPopup` every frame**: This keeps the popup perpetually open because the library interprets it as a continuous open request. Call it only on the specific event (button press, right-click) that triggers the popup.
- **Mismatched ID depth**: Opening in one container and beginning in another breaks the ID lookup.
- **Forgetting `EndPopup`**: Skipping this after a successful `BeginPopup` corrupts the internal `BeginPopupStack` and causes subsequent popups to behave erratically.
- **Wrong mouse button defaults**: Prior to v1.92.6, context menus defaulted to the left button; current versions use the right button. Explicitly pass `ImGuiPopupFlags_MouseButtonLeft` or `ImGuiPopupFlags_MouseButtonRight` if you need specific behavior.

## Practical Code Examples

### Simple Modal Dialog

```cpp
if (ImGui::Button("Show modal"))
    ImGui::OpenPopup("my_modal");

if (ImGui::BeginPopupModal("my_modal", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
{
    ImGui::Text("This is a modal dialog.");
    if (ImGui::Button("Close"))
        ImGui::CloseCurrentPopup();
    ImGui::EndPopup();
}

```

*The modal blocks all interaction behind it and automatically dims the background.*

### Context Menu on an Item

```cpp
ImGui::Text("Right-click me");
if (ImGui::BeginPopupContextItem())
{
    if (ImGui::MenuItem("Delete")) { /* handle delete */ }
    if (ImGui::MenuItem("Rename")) { /* handle rename */ }
    ImGui::EndPopup();
}

```

*No explicit `OpenPopup` call is required—the helper handles the open and begin logic.*

### Custom Popup from a Button

```cpp
if (ImGui::Button("Open custom popup"))
    ImGui::OpenPopup("custom_popup");

if (ImGui::BeginPopup("custom_popup"))
{
    ImGui::Text("Custom content");
    if (ImGui::Button("Close"))
        ImGui::CloseCurrentPopup();
    ImGui::EndPopup();
}

```

*Call `OpenPopup` only on the button press to create dropdown-like behavior.*

### Context Menu on Void (Background)

```cpp
if (ImGui::BeginPopupContextVoid())
{
    if (ImGui::MenuItem("Add item")) { /* handle add */ }
    ImGui::EndPopup();
}

```

*Triggers when the user right-clicks the background where no widget is present.*

## Key Implementation Files

Understanding the popup system requires familiarity with three core files:

- **[imgui.h](https://github.com/ocornut/imgui/blob/master/imgui.h)**: Public API declarations for `BeginPopup*`, `OpenPopup*`, and context-menu helpers.
- **[imgui_internal.h](https://github.com/ocornut/imgui/blob/master/imgui_internal.h)**: Internal structures including `ImGuiPopupData` and `BeginPopupStack`.
- **[imgui.cpp](https://github.com/ocornut/imgui/blob/master/imgui.cpp)**: Implementation of `BeginPopupEx`, modal handling, and stack logic.

Real-world usage examples are available in **[examples/example_sdl2_opengl3/main.cpp](https://github.com/ocornut/imgui/blob/master/examples/example_sdl2_opengl3/main.cpp)** and **[examples/example_win32_directx11/main.cpp](https://github.com/ocornut/imgui/blob/master/examples/example_win32_directx11/main.cpp)**.

## Summary

- **Popups are stack-based**: State lives in `g.BeginPopupStack` and is evaluated per-frame.
- **Match your IDs**: `OpenPopup` and `BeginPopup` must share the same ID stack depth.
- **Trigger once**: Call `OpenPopup` only on the event that opens the popup, not every frame.
- **Use helpers**: `BeginPopupContextItem` and `BeginPopupContextVoid` simplify context menu creation.
- **Always pair**: Every successful `BeginPopup` or `BeginPopupModal` must be followed by `EndPopup`.

## Frequently Asked Questions

### Why does my popup fail to appear even though I called OpenPopup?

This typically occurs due to an **ID stack mismatch**. If you call `OpenPopup` inside a `TreeNode`, `PushID`, or `BeginGroup` block, you must call the corresponding `BeginPopup` inside that same block before the ID is popped. The hashed identifier includes the current stack scope, so differing depths produce different IDs.

### What is the difference between BeginPopup and BeginPopupModal?

`BeginPopup` creates a standard popup window that closes when the user clicks outside of it. `BeginPopupModal` (at **[imgui.h#L849](https://github.com/ocornut/imgui/blob/master/imgui.h#L849)**) creates a modal dialog that blocks interaction with all other windows, automatically dims the background, and typically requires an explicit user action (button press or `CloseCurrentPopup`) to dismiss.

### How do I open a context menu with the left mouse button instead of right?

Pass `ImGuiPopupFlags_MouseButtonLeft` to the `popup_flags` parameter of `BeginPopupContextItem`, `BeginPopupContextWindow`, or `BeginPopupContextVoid`. For example: `BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)`. If using the manual `OpenPopup` approach, call `OpenPopupOnItemClick` with the desired button flag.

### When should I call CloseCurrentPopup versus letting the popup close automatically?

Call `CloseCurrentPopup` (defined at **[imgui.h#L62](https://github.com/ocornut/imgui/blob/master/imgui.h#L62)**) when you need to dismiss the popup immediately upon a specific user action, such as clicking a "Confirm" or "Cancel" button inside a modal dialog. For non-modal popups, you can simply let the user click outside the window to close it naturally, which terminates the popup when `BeginPopup` returns `false` on the next frame.