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

> Learn to serialize and restore Dear ImGui UI state using its built-in INI file system. Control window positions, sizes, and table layouts with simple API calls.

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

---

**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`](https://github.com/ocornut/imgui/blob/main/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.

```cpp
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`](https://github.com/ocornut/imgui/blob/main/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`](https://github.com/ocornut/imgui/blob/main/imgui.cpp):

```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`](https://github.com/ocornut/imgui/blob/main/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`](https://github.com/ocornut/imgui/blob/main/imgui.cpp):

```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`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) near line 2600.

```cpp
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:

```cpp
// 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`](https://github.com/ocornut/imgui/blob/main/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.