How to Serialize and Restore Dear ImGui UI State Using the INI File System

Dear ImGui provides a built-in text-based INI system that automatically persists window positions, sizes, and table layouts to disk or memory, exposing manual control through SaveIniSettingsToDisk and LoadIniSettingsFromMemory APIs.

The Dear ImGui library (ocornut/imgui) ships with a lightweight serialization framework that captures UI state without requiring external dependencies. This system handles window geometry, collapsed states, and table column widths by default, while also supporting custom application data through a pluggable handler architecture.

How the Dear ImGui INI System Works

The persistence layer revolves around the ImGuiSettingsHandler structure registered during context creation. In imgui.cpp around line 2500, ImGui::CreateContext() automatically registers the default handler via ImGui::AddSettingsHandler(&ImGuiSettingsHandler_Ini). This handler implements three critical callbacks:

  • ReadOpen: Allocates storage when encountering a new section header like [Window][Name]
  • ReadLine: Parses key-value pairs (e.g., Pos=100,200) into the corresponding window or table structures
  • WriteAll: Serializes current state into a text buffer when saving

The system assigns a unique ImGuiID to each window and table, using this identifier to generate section headers that survive across application restarts.

Automatic INI Persistence

By default, Dear ImGui attempts to manage state automatically through the io.IniFilename field.

ImGuiIO& io = ImGui::GetIO();
io.IniFilename = "imgui.ini";  // Set to nullptr to disable automatic saving

When this field points to a valid path, the framework automatically writes the INI file at intervals controlled by io.IniSavingRate (default: typically once per 30 seconds or on shutdown). The write operation occurs in imgui.cpp near line 3000, where the engine iterates through all registered handlers and calls their WriteAll function.

To disable automatic persistence entirely, set io.IniFilename = nullptr or set the configuration flag ImGuiConfigFlags_NoIni in io.ConfigFlags.

Manual File-Based Serialization

For explicit control over when state commits to disk, use the file-based API defined in imgui.cpp:

// At application startup, before creating windows
ImGui::LoadIniSettingsFromDisk("my_app_state.ini");

// Main loop - automatic handling occurs here if io.IniFilename is set

// On application exit or explicit user request
ImGui::SaveIniSettingsToDisk("my_app_state.ini");

ImGui::SaveIniSettingsToDisk(const char* filename) (located around line 3000 in imgui.cpp) triggers an immediate write of all registered settings handlers to the specified path. Note: If filename is NULL, the function defaults to io.IniFilename.

ImGui::LoadIniSettingsFromDisk(const char* filename) (around line 3100) parses the file and dispatches lines to the appropriate handler's ReadOpen and ReadLine callbacks. Call this before defining your UI layout to restore previous positions.

Memory-Based Serialization

For applications that cannot rely on local filesystem access (networked configs, sandboxed environments, or cloud synchronization), Dear ImGui offers memory-based alternatives in imgui.cpp:

// Save current state to memory buffer
size_t dataSize = 0;
const char* iniData = ImGui::SaveIniSettingsToMemory(&dataSize);

// Store buffer somewhere (database, network packet, etc.)
std::string cachedSettings(iniData, dataSize);

// Later, restore from that buffer
ImGui::LoadIniSettingsFromMemory(cachedSettings.c_str(), cachedSettings.size());

SaveIniSettingsToMemory(size_t* out_size) (around line 3200) returns a pointer to an internal text buffer containing the serialized state. This buffer remains valid until the next call to any Save function or until the context is destroyed.

LoadIniSettingsFromMemory(const char* data, size_t size) (around line 3300) parses the provided buffer exactly like the disk variant, restoring all window positions, table widths, and custom handler data.

Custom Settings Handlers

To persist application-specific data alongside ImGui's window state, register a custom ImGuiSettingsHandler via ImGui::AddSettingsHandler(). This appears in imgui.cpp near line 2600.

struct AppConfig {
    int themeId = 0;
    bool showAdvanced = false;
} g_Config;

static void MyReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) {
    return &g_Config;  // Return pointer to data structure for this section
}

static void MyReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) {
    AppConfig* cfg = (AppConfig*)entry;
    int i;
    if (sscanf(line, "Theme=%d", &i) == 1) cfg->themeId = i;
    if (sscanf(line, "Advanced=%d", &i) == 1) cfg->showAdvanced = (i != 0);
}

static void MyWriteAll(ImGuiContext*, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) {
    buf->appendf("[%s][Data]\n", handler->TypeName);
    buf->appendf("Theme=%d\n", g_Config.themeId);
    buf->appendf("Advanced=%d\n", g_Config.showAdvanced ? 1 : 0);
    buf->append("\n");
}

void SetupCustomHandler() {
    ImGuiSettingsHandler handler;
    handler.TypeName = "MyApp";
    handler.ReadOpenFn = MyReadOpen;
    handler.ReadLineFn = MyReadLine;
    handler.WriteAllFn = MyWriteAll;
    ImGui::AddSettingsHandler(&handler);
}

Custom sections appear in the final INI file as [MyApp][Data] blocks, interleaved with standard [Window] entries.

Forcing Immediate State Updates

When you programmatically modify window properties (via SetNextWindowPos or SetNextWindowSize), Dear ImGui may not immediately mark the INI as dirty. To force a write on the next automatic save interval, or to trigger an immediate manual save:

// Programmatically move window
ImGui::SetNextWindowPos(ImVec2(500, 300));
ImGui::Begin("Tools");
ImGui::End();

// Mark settings as dirty to ensure they persist
ImGui::MarkIniSettingsDirty();

// Optional: immediate save
ImGui::SaveIniSettingsToDisk(ImGui::GetIO().IniFilename);

MarkIniSettingsDirty() (referenced in imgui_widgets.cpp around line 10457) sets an internal flag that causes the settings system to regenerate the INI buffer on the next save cycle. Call this after any programmatic UI changes that should survive application restart.

Summary

  • Dear ImGui automatically handles UI persistence through a built-in INI system registered in ImGui::CreateContext()
  • File-based workflows use SaveIniSettingsToDisk() and LoadIniSettingsFromDisk(), controlled via io.IniFilename
  • Memory-based workflows use SaveIniSettingsToMemory() and LoadIniSettingsFromMemory() for non-filesystem storage
  • Custom data extends the system through ImGui::AddSettingsHandler() with ReadOpen, ReadLine, and WriteAll callbacks
  • Immediate persistence is achieved through MarkIniSettingsDirty() followed by an explicit save call

Frequently Asked Questions

Can I disable the automatic INI saving entirely?

Yes. Set ImGui::GetIO().IniFilename = nullptr before the first frame, or set the ImGuiConfigFlags_NoIni flag in io.ConfigFlags. This prevents the automatic timer-based writes that occur approximately every 30 seconds, though you can still manually call SaveIniSettingsToDisk() when desired.

How do I store my application's settings in the same INI file as Dear ImGui?

Register a custom settings handler. Implement an ImGuiSettingsHandler structure with TypeName, ReadOpenFn, ReadLineFn, and WriteAllFn callbacks, then pass it to ImGui::AddSettingsHandler(). Your data appears as a custom section (e.g., [MyApp][Settings]) in the same file alongside [Window] entries.

What is the difference between disk-based and memory-based serialization?

Disk-based functions (SaveIniSettingsToDisk, LoadIniSettingsFromDisk) read from and write to filesystem paths directly, handling file I/O internally. Memory-based functions (SaveIniSettingsToMemory, LoadIniSettingsFromMemory) operate on null-terminated character buffers that you manage, allowing storage in databases, network packets, or encrypted containers instead of local files.

When should I call MarkIniSettingsDirty?

Call it after programmatically modifying window state that should persist to the INI file. Dear ImGui automatically marks settings dirty when users interactively move or resize windows, but programmatic changes via SetNextWindowPos or direct manipulation of table column widths may not trigger the dirty flag. Calling MarkIniSettingsDirty() ensures the next save operation captures these changes.

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 →