# How to Create a Custom Platform Backend for Dear ImGui: A Complete Implementation Guide

> Learn to create a custom platform backend for Dear ImGui. Implement window management and input handling to separate concerns and manage platform resources effectively for your application.

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

---

**Dear ImGui separates platform concerns (window management, input handling) from rendering by requiring backends to populate the `ImGuiIO` structure with display metrics, translate native OS events into ImGui input calls, and manage platform-specific resources through a dedicated backend data structure.**

Dear ImGui (ocornut/imgui) uses a platform-agnostic core that delegates all window system interactions to pluggable backends. Creating a custom platform backend involves implementing a minimal C++ interface that bridges your native windowing system with ImGui's input abstraction layer. This guide references the actual source code in the official repository to show you exactly how to build a production-ready backend.

## Understanding the Platform Backend Architecture

A platform backend acts as a translation layer between your operating system's windowing API and Dear ImGui's internal input system. The architecture centers on the **`ImGuiIO`** structure, which you populate each frame with timing data, display dimensions, and input events.

Every platform backend must maintain a **backend data structure** (following the pattern of `ImGui_ImplWin32_Data` in [`imgui_impl_win32.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_win32.cpp)) that persists between frames. This struct stores native window handles, timing counters, mouse state, and optional gamepad handles. You attach this structure to `io.BackendPlatformUserData` during initialization and clear it during shutdown.

The backend also advertises its capabilities through **`ImGuiBackendFlags`**. Common flags include `ImGuiBackendFlags_HasMouseCursors` and `ImGuiBackendFlags_HasSetMousePos`, which you set in `io.BackendFlags` during initialization to inform ImGui that your backend can manipulate the system cursor.

## Step 1: Create the Backend Data Structure

Define a data structure that holds all platform-specific state. This pattern appears consistently across official backends like [`imgui_impl_win32.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_win32.cpp) (lines 62-80) and [`imgui_impl_glfw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_glfw.cpp).

```cpp
// imgui_impl_mybackend.h
#pragma once
#include "imgui.h"

struct MyBackendData
{
    MyWindowHandle  Window;
    double          Time;
    float           MousePos[2];
    bool            MouseButtons[5];
    // Add clipboard handles, cursor handles, etc.
};

// Function declarations
IMGUI_IMPL_API bool ImGui_ImplMyBackend_Init(void* native_window);
IMGUI_IMPL_API void ImGui_ImplMyBackend_Shutdown();
IMGUI_IMPL_API void ImGui_ImplMyBackend_NewFrame();
IMGUI_IMPL_API void ImGui_ImplMyBackend_HandleEvent(const MyEvent& ev);

```

Store this structure in `io.BackendPlatformUserData` to make your backend re-entrant for multiple ImGui contexts.

## Step 2: Implement Initialization

The initialization function must allocate your backend data, set the backend name, and advertise supported capabilities. Reference [`imgui_impl_win32.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_win32.cpp) (lines 62-80) for the exact initialization sequence.

```cpp
bool ImGui_ImplMyBackend_Init(void* native_window)
{
    ImGuiIO& io = ImGui::GetIO();
    IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized!");
    
    MyBackendData* bd = IM_NEW(MyBackendData)();
    io.BackendPlatformUserData = (void*)bd;
    io.BackendPlatformName = "imgui_impl_mybackend";
    
    // Advertise capabilities
    io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;
    io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;
    
    bd->Window = (MyWindowHandle)native_window;
    bd->Time = GetPlatformTime(); // Platform-specific timer
    
    return true;
}

```

## Step 3: Handle Per-Frame Updates

Each frame, the backend must update timing information, display size, and mouse cursor state. The `NewFrame` function in [`imgui_impl_win32.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_win32.cpp) (lines 219-267) demonstrates this pattern.

```cpp
void ImGui_ImplMyBackend_NewFrame()
{
    ImGuiIO& io = ImGui::GetIO();
    MyBackendData* bd = (MyBackendData*)io.BackendPlatformUserData;
    IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplMyBackend_Init()?");
    
    // Update display size
    io.DisplaySize = GetWindowSize(bd->Window);
    
    // Update delta time
    double current_time = GetPlatformTime();
    io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f/60.0f);
    bd->Time = current_time;
    
    // Update mouse cursor if requested
    if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
        return;
    
    ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
    if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
        HideSystemCursor();
    else
        SetSystemCursor(imgui_cursor);
}

```

## Step 4: Translate Native Events

The critical responsibility is converting your platform's native events into ImGui input calls. Create a handler function that your application calls for each window event, following the event translation patterns in [`imgui_impl_glfw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_glfw.cpp) and [`imgui_impl_sdl2.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_sdl2.cpp).

```cpp
void ImGui_ImplMyBackend_HandleEvent(const MyEvent& ev)
{
    ImGuiIO& io = ImGui::GetIO();
    
    switch (ev.type)
    {
        case MyEvent::MouseMove:
            io.AddMousePosEvent((float)ev.x, (float)ev.y);
            break;
        case MyEvent::MouseButtonDown:
            io.AddMouseButtonEvent(ev.button, true);
            break;
        case MyEvent::MouseButtonUp:
            io.AddMouseButtonEvent(ev.button, false);
            break;
        case MyEvent::MouseWheel:
            io.AddMouseWheelEvent(ev.wheel_x, ev.wheel_y);
            break;
        case MyEvent::KeyDown:
            io.AddKeyEvent(ev.key, true);
            break;
        case MyEvent::KeyUp:
            io.AddKeyEvent(ev.key, false);
            break;
        case MyEvent::Char:
            io.AddInputCharacterUTF16(ev.codepoint);
            break;
    }
}

```

Call `io.AddInputCharacterUTF16()` for text input rather than direct key events to properly handle international keyboards and dead keys.

## Step 5: Implement Shutdown

Cleanup must release all allocated resources and clear backend-specific `ImGuiIO` fields to prevent dangling pointers. Reference [`imgui_impl_win32.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_win32.cpp) (lines 400-426).

```cpp
void ImGui_ImplMyBackend_Shutdown()
{
    ImGuiIO& io = ImGui::GetIO();
    MyBackendData* bd = (MyBackendData*)io.BackendPlatformUserData;
    
    if (bd)
    {
        // Free cursors, unload DLLs, etc.
        IM_DELETE(bd);
        io.BackendPlatformUserData = nullptr;
        io.BackendPlatformName = nullptr;
        io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos);
    }
}

```

## Step 6: Integrate Into Your Main Loop

Wire the backend into your application's event loop, ensuring correct ordering of ImGui function calls. The `examples/` folder in the repository contains reference implementations like [`example_win32_opengl3/main.cpp`](https://github.com/ocornut/imgui/blob/main/example_win32_opengl3/main.cpp).

```cpp
// Initialization
MyWindow* window = CreateNativeWindow();
ImGui_ImplMyBackend_Init(window);
ImGui_ImplOpenGL3_Init("#version 330");

// Main loop
while (!ShouldClose(window))
{
    // Process events - forward to backend handler
    PollEvents(window, ImGui_ImplMyBackend_HandleEvent);
    
    // Start ImGui frame
    ImGui_ImplMyBackend_NewFrame();
    ImGui_ImplOpenGL3_NewFrame();
    ImGui::NewFrame();
    
    // Build UI here
    ImGui::ShowDemoWindow();
    
    // Render
    ImGui::Render();
    int display_w, display_h;
    GetFramebufferSize(window, &display_w, &display_h);
    glViewport(0, 0, display_w, display_h);
    glClear(GL_COLOR_BUFFER_BIT);
    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
    SwapBuffers(window);
}

// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplMyBackend_Shutdown();

```

## Optional: Clipboard, Gamepad, and Multi-Viewport

**Clipboard support** requires setting `io.SetClipboardTextFn` and `io.GetClipboardTextFn` callbacks. The Win32 backend (lines 120-150) shows a reference implementation using native clipboard APIs.

**Gamepad support** involves loading XInput or similar libraries dynamically and pushing controller states via `io.AddKeyEvent()` for mapped buttons and `io.AddAnalogEvent()` for axes. See the `#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD` section in [`imgui_impl_win32.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_win32.cpp).

**Multi-viewport support** requires additional `ImGuiPlatformIO` callbacks: `Platform_CreateWindow`, `Platform_DestroyWindow`, and `Platform_ShowWindow`. These enable ImGui to create floating tool windows outside the main application window. The Win32 backend (lines 300-340) contains the platform window management implementation.

## Summary

- **Backend data persistence**: Store platform state in a dedicated structure attached to `io.BackendPlatformUserData`, following the `ImGui_ImplWin32_Data` pattern.
- **Capability advertisement**: Set `ImGuiBackendFlags` in `io.BackendFlags` during initialization to indicate support for mouse cursors and cursor positioning.
- **Event translation**: Convert native window events to ImGui input using `io.AddMousePosEvent()`, `io.AddKeyEvent()`, and `io.AddInputCharacterUTF16()`.
- **Frame updates**: Update `io.DisplaySize` and `io.DeltaTime` every frame in your `NewFrame` function.
- **Resource cleanup**: Release all handles and clear `ImGuiIO` backend-specific fields in your `Shutdown` function.

## Frequently Asked Questions

### What is the difference between a platform backend and a renderer backend in Dear ImGui?

A **platform backend** handles window system integration—managing input events, window focus, clipboard, and timing—while a **renderer backend** handles graphics API calls to draw ImGui's vertex buffers. You typically combine one platform backend (e.g., Win32, GLFW, SDL) with one renderer backend (e.g., OpenGL3, DirectX11, Vulkan) to create a complete implementation.

### How do I handle high-DPI displays in a custom platform backend?

Update `io.DisplayFramebufferScale` with the appropriate DPI scaling factors (e.g., obtained from `GetDpiForWindow` on Windows or `glfwGetMonitorContentScale` on GLFW). ImGui uses this scale to adjust font rasterization and vertex projection. You must also scale `io.DisplaySize` by the inverse of the framebuffer scale so that ImGui operates in logical screen coordinates.

### Can I support multiple windows with one platform backend?

Yes, but you must implement multi-viewport support by populating the `ImGuiPlatformIO` structure with callbacks for `Platform_CreateWindow`, `Platform_DestroyWindow`, and `Platform_ShowWindow`. The backend data structure should track multiple window handles, and you must ensure each viewport's `PlatformUserData` points to the correct native window. Reference [`imgui_impl_win32.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_win32.cpp) (lines 300-340) for the platform window management implementation.

### What is the correct order of function calls in the main loop?

First, poll and translate native events using your backend's event handler. Second, call your platform backend's `NewFrame` function to update `io.DeltaTime` and `io.DisplaySize`. Third, call the renderer backend's `NewFrame` function. Fourth, call `ImGui::NewFrame()`. After building the UI, call `ImGui::Render()` followed by the renderer's `RenderDrawData` function. Shutdown must occur in reverse order: renderer first, then platform.