Documentation Index Fetch the complete documentation index at: https://mintlify.com/ocornut/imgui/llms.txt
Use this file to discover all available pages before exploring further.
Overview
This guide covers recommended patterns and practices for building maintainable, performant Dear ImGui applications.
Core Principles
Dear ImGui uses an immediate mode API where UI is rebuilt every frame:
// Good: Emit UI every frame
while (running) {
ImGui :: NewFrame ();
if ( ImGui :: Button ( "Click me" )) {
DoSomething ();
}
ImGui :: Render ();
}
Unlike retained-mode UIs, you don’t create widgets once and modify them later. Instead, you emit them fresh each frame.
Application Owns Data
// Good: Application owns the data
struct AppData {
float slider_value = 0.5 f ;
char text_buffer [ 256 ] = "Hello" ;
bool checkbox_state = true ;
};
AppData data;
void RenderUI () {
ImGui :: SliderFloat ( "Value" , & data . slider_value , 0.0 f , 1.0 f );
ImGui :: InputText ( "Text" , data . text_buffer , sizeof ( data . text_buffer ));
ImGui :: Checkbox ( "Enable" , & data . checkbox_state );
}
ID Management
Understanding IDs
Every interactive widget needs a unique ID. Most common mistake: using duplicate IDs.
// BAD: Duplicate IDs
for ( int i = 0 ; i < items . size (); i ++ ) {
ImGui :: Button ( "Delete" ); // All buttons have same ID!
}
// GOOD: Use PushID/PopID
for ( int i = 0 ; i < items . size (); i ++ ) {
ImGui :: PushID (i);
ImGui :: Button ( "Delete" ); // Each button has unique ID
ImGui :: PopID ();
}
// GOOD: Use ## suffix
for ( int i = 0 ; i < items . size (); i ++ ) {
char label [ 32 ];
sprintf (label, "Delete## %d " , i);
ImGui :: Button (label); // Label shows "Delete", ID is "Delete##0", etc.
}
Use the built-in tool to debug ID issues:
ImGui :: ShowIDStackToolWindow ();
// Hover over widgets to see their ID path
Window Management
Always Match Begin/End
// ALWAYS call End() even if Begin() returns false
bool open = true ;
if ( ImGui :: Begin ( "My Window" , & open)) {
// Window is not collapsed, render content
ImGui :: Text ( "Content" );
}
ImGui :: End (); // ALWAYS call this
Begin/End and BeginChild/EndChild must always be paired, unlike other BeginXXX/EndXXX functions.
Window Flags
Use appropriate flags for your use case:
// Tool window without decorations
ImGui :: Begin ( "Tools" , nullptr ,
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove);
// Fixed-size window
ImGui :: SetNextWindowSize ( ImVec2 ( 400 , 300 ), ImGuiCond_Always);
ImGui :: Begin ( "Fixed" );
// Window with menu bar
ImGui :: Begin ( "Main" , nullptr , ImGuiWindowFlags_MenuBar);
if ( ImGui :: BeginMenuBar ()) {
if ( ImGui :: BeginMenu ( "File" )) {
ImGui :: MenuItem ( "Open" );
ImGui :: EndMenu ();
}
ImGui :: EndMenuBar ();
}
Layout Best Practices
Use GetContentRegionAvail
// Good: Responsive layout
ImVec2 avail = ImGui :: GetContentRegionAvail ();
ImGui :: Button ( "Full Width" , ImVec2 ( avail . x , 0 ));
// Good: Split layout
ImGui :: BeginChild ( "Left" , ImVec2 ( avail . x * 0.3 f , 0 ));
// Left content
ImGui :: EndChild ();
ImGui :: SameLine ();
ImGui :: BeginChild ( "Right" , ImVec2 ( 0 , 0 ));
// Right content
ImGui :: EndChild ();
Proper Spacing
// Horizontal layout
ImGui :: Button ( "Button 1" );
ImGui :: SameLine ();
ImGui :: Button ( "Button 2" );
// Custom spacing
ImGui :: Button ( "Button 1" );
ImGui :: SameLine ( 0.0 f , 20.0 f ); // 20px spacing
ImGui :: Button ( "Button 2" );
// Vertical spacing
ImGui :: Spacing ();
ImGui :: Separator ();
ImGui :: Dummy ( ImVec2 ( 0 , 20 )); // 20px vertical space
Tables for Complex Layouts
if ( ImGui :: BeginTable ( "layout" , 2 , ImGuiTableFlags_Resizable)) {
ImGui :: TableSetupColumn ( "Left" , ImGuiTableColumnFlags_WidthFixed, 200.0 f );
ImGui :: TableSetupColumn ( "Right" , ImGuiTableColumnFlags_WidthStretch);
ImGui :: TableNextRow ();
ImGui :: TableNextColumn ();
// Left content
ImGui :: TableNextColumn ();
// Right content
ImGui :: EndTable ();
}
Check WantCapture Flags
void HandleInput () {
ImGuiIO & io = ImGui :: GetIO ();
// ALWAYS pass input to ImGui
io . AddMouseButtonEvent (button, pressed);
io . AddKeyEvent (key, pressed);
// Only handle in app if ImGui doesn't want it
if ( ! io . WantCaptureMouse ) {
// Handle mouse in application
}
if ( ! io . WantCaptureKeyboard ) {
// Handle keyboard in application
}
}
Always pass input to ImGui first, then check WantCapture flags before handling in your application.
Style Management
Push/Pop Pattern
// Good: Balanced push/pop
ImGui :: PushStyleColor (ImGuiCol_Button, ImVec4 ( 1 , 0 , 0 , 1 ));
ImGui :: PushStyleVar (ImGuiStyleVar_FrameRounding, 10.0 f );
ImGui :: Button ( "Styled Button" );
ImGui :: PopStyleVar ();
ImGui :: PopStyleColor ();
// Good: Count-based popping
int style_count = 0 ;
if (condition) {
ImGui :: PushStyleColor (ImGuiCol_Text, color);
style_count ++ ;
}
if (other_condition) {
ImGui :: PushStyleVar (ImGuiStyleVar_Alpha, 0.5 f );
style_count ++ ;
}
// UI code
if (style_count > 0 ) {
ImGui :: PopStyleVar (style_count);
ImGui :: PopStyleColor (style_count);
}
Initialize Style Once
// Good: Set style at startup
void InitializeImGui () {
ImGui :: CreateContext ();
ImGuiStyle & style = ImGui :: GetStyle ();
style . WindowRounding = 5.0 f ;
style . FrameRounding = 3.0 f ;
style . Colors [ImGuiCol_WindowBg] = ImVec4 ( 0.1 f , 0.1 f , 0.1 f , 1.0 f );
// Initialize backends...
}
// Don't modify ImGuiStyle mid-frame unless using Push/Pop
Minimize Window Count
// Good: Use child windows for sections
ImGui :: Begin ( "Main Window" );
ImGui :: BeginChild ( "Section1" , ImVec2 ( 0 , 200 ));
// Section 1 content
ImGui :: EndChild ();
ImGui :: BeginChild ( "Section2" );
// Section 2 content
ImGui :: EndChild ();
ImGui :: End ();
Early Exit for Collapsed Windows
if ( ImGui :: Begin ( "My Window" , & open)) {
// Only execute this if window is visible
ExpensiveRenderCode ();
}
ImGui :: End ();
Use ListClipper for Large Lists
ImGuiListClipper clipper;
clipper . Begin ( items . size ());
while ( clipper . Step ()) {
for ( int i = clipper . DisplayStart ; i < clipper . DisplayEnd ; i ++ ) {
ImGui :: Text ( "Item %d : %s " , i, items [i]. name );
}
}
// Only visible items are processed!
Reduce Draw Calls
// Good: Minimize style changes
ImGui :: PushStyleColor (ImGuiCol_Button, red_color);
for ( int i = 0 ; i < 10 ; i ++ ) {
ImGui :: Button ( "Red Button" );
}
ImGui :: PopStyleColor ();
// Bad: Changing style every iteration causes more draw calls
for ( int i = 0 ; i < 10 ; i ++ ) {
ImGui :: PushStyleColor (ImGuiCol_Button, red_color);
ImGui :: Button ( "Red Button" );
ImGui :: PopStyleColor ();
}
Error Prevention
Static Buffers for InputText
// Good: Static or persistent buffer
static char buffer [ 256 ] = "" ;
ImGui :: InputText ( "Input" , buffer, sizeof (buffer));
// Bad: Local buffer that loses data
void MyFunction () {
char buffer [ 256 ] = "" ; // Resets every frame!
ImGui :: InputText ( "Input" , buffer, sizeof (buffer));
}
Static Arrays for Persistent Data
// Good: Persistent state
static bool selected [ 100 ] = {};
for ( int i = 0 ; i < 100 ; i ++ ) {
ImGui :: Selectable ( items [i]. name , & selected [i]);
}
RAII Helpers
Create helpers for automatic cleanup:
struct StyleColorGuard {
StyleColorGuard ( ImGuiCol idx , ImVec4 color ) {
ImGui :: PushStyleColor (idx, color);
}
~StyleColorGuard () {
ImGui :: PopStyleColor ();
}
};
// Use it
void RenderButton () {
StyleColorGuard guard (ImGuiCol_Button, red);
ImGui :: Button ( "Red Button" );
} // Automatically pops on scope exit
Organization
Modular UI Functions
void RenderMainMenu () {
if ( ImGui :: BeginMainMenuBar ()) {
if ( ImGui :: BeginMenu ( "File" )) {
if ( ImGui :: MenuItem ( "Open" , "Ctrl+O" )) OpenFile ();
if ( ImGui :: MenuItem ( "Save" , "Ctrl+S" )) SaveFile ();
ImGui :: EndMenu ();
}
ImGui :: EndMainMenuBar ();
}
}
void RenderPropertiesPanel ( Entity * entity ) {
ImGui :: Begin ( "Properties" );
if (entity) {
ImGui :: InputText ( "Name" , entity -> name , sizeof ( entity -> name ));
ImGui :: DragFloat3 ( "Position" , & entity -> position . x );
}
ImGui :: End ();
}
void RenderUI () {
RenderMainMenu ();
RenderPropertiesPanel (selected_entity);
}
Separate Data from UI
// Good: Clear separation
struct Settings {
float volume = 0.5 f ;
bool fullscreen = false ;
int quality = 2 ;
};
Settings g_settings;
void RenderSettingsUI () {
ImGui :: SliderFloat ( "Volume" , & g_settings . volume , 0.0 f , 1.0 f );
ImGui :: Checkbox ( "Fullscreen" , & g_settings . fullscreen );
ImGui :: Combo ( "Quality" , & g_settings . quality , "Low \0 Medium \0 High \0 " );
}
void ApplySettings () {
SetVolume ( g_settings . volume );
SetFullscreen ( g_settings . fullscreen );
SetQuality ( g_settings . quality );
}
Testing and Debugging
Use Demo Window
// Always keep demo available during development
#ifndef NDEBUG
static bool show_demo = true ;
if (show_demo) {
ImGui :: ShowDemoWindow ( & show_demo);
}
#endif
Metrics Window
// Enable metrics for debugging
static bool show_metrics = false ;
if ( ImGui :: IsKeyPressed (ImGuiKey_F12)) {
show_metrics = ! show_metrics;
}
if (show_metrics) {
ImGui :: ShowMetricsWindow ( & show_metrics);
}
Assert on Errors
// Use IM_ASSERT for development
IM_ASSERT (data != nullptr );
IM_ASSERT (index < items . size ());
Common Pitfalls
Always pair Begin() with End(), even if Begin() returns false. // Wrong
if ( ImGui :: Begin ( "Window" )) {
ImGui :: Text ( "Content" );
ImGui :: End (); // Only called if Begin returns true!
}
// Correct
if ( ImGui :: Begin ( "Window" )) {
ImGui :: Text ( "Content" );
}
ImGui :: End (); // Always called
Most common beginner mistake. Use PushID or ## suffix.
Modifying Style Mid-Frame
Use PushStyleVar/PushStyleColor for temporary changes, not direct ImGuiStyle modification.
Not Handling UTF-8 Correctly
Use u8"" prefix for non-ASCII strings and ensure source files are UTF-8.
Dear ImGui layouts are dynamic. Use GetContentRegionAvail() instead of hardcoded sizes.
See Also