How to Map and Use ImTextureID for Rendering Custom Images in Dear ImGui

ImTextureID is a 64-bit opaque handle that bridges your graphics API's native texture with Dear ImGui's draw commands, requiring you to cast your backend's texture handle (such as GLuint or ID3D11ShaderResourceView*) to this type before passing it to ImGui::Image() or storing it in ImDrawCmd.

Dear ImGui (ocornut/imgui) remains graphics API agnostic by treating all textures as opaque identifiers through the ImTextureID type. To render custom images, you must convert your GPU's native texture handle into this portable format and pass it through ImGui's widget API or direct draw list commands.

Understanding ImTextureID and ImTextureRef

What Is ImTextureID?

According to [imgui.h](https://github.com/ocornut/imgui/blob/master/imgui.h#L3220-L3230), ImTextureID is defined as a 64-bit unsigned integer by default:

#ifndef ImTextureID
typedef ImU64 ImTextureID;          // default: 64-bit value (pointer or integer)
#endif

This typedef allows the Dear ImGui core to store texture references without knowing the specifics of your graphics API. Whether you use DirectX 11, OpenGL, Vulkan, or SDL-GPU, the backend converts its native texture handle into this 64-bit value.

The Role of ImTextureRef in v1.92+

Starting with version 1.92, Dear ImGui wraps texture identifiers in the ImTextureRef struct. As defined in [imgui.h lines 350-380](https://github.com/ocornut/imgui/blob/master/imgui.h#L350-L380), this structure holds either a raw ImTextureID or a pointer to internal ImTextureData. When you pass a texture to ImGui::Image(), the API implicitly constructs an ImTextureRef from your identifier, storing it inside ImDrawCmd::TexRef.

Backend Integration: Converting Native Handles

Your rendering backend must perform two critical steps: create the native GPU texture, then cast or convert the handle to ImTextureID.

DirectX 11 Implementation

In [imgui_impl_dx11.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx11.cpp#L1-L30), the backend casts the shader resource view pointer:

ID3D11ShaderResourceView* srv = /* your texture view */;
ImTextureID tex_id = (ImTextureID)(intptr_t)srv;      // ImGui expects ImTextureID

OpenGL Implementation

For OpenGL, the pattern is identical but uses GLuint handles. As shown in the backend examples, you cast the texture name:

GLuint tex = LoadGLTexture("image.png");              // Your loader returns GLuint
ImTextureID my_id = (ImTextureID)(intptr_t)tex;        // Safe cast through intptr_t

The backend must then register this identifier by calling ImTextureData::SetTexID() during the texture creation phase, storing the value inside ImDrawCmd::TexRef before any drawing occurs.

Rendering Custom Images in Your UI

Once you have a valid ImTextureID, you can render it using several methods.

Using ImGui::Image()

The simplest approach uses the high-level ImGui::Image() function. The demo code in [imgui_demo.cpp lines 1999-2002](https://github.com/ocornut/imgui/blob/master/imgui_demo.cpp#L1999-L2002) demonstrates this with the font atlas:

ImFontAtlas* atlas = ImGui::GetIO().Fonts;
ImTextureRef my_tex_id = atlas->TexRef;               // Wraps the ImTextureID
float my_tex_w = (float)atlas->TexData->Width;
float my_tex_h = (float)atlas->TexData->Height;

ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h));  // Renders the texture

Creating Clickable Image Buttons

For interactive elements, use ImGui::ImageButton(). Critical: Version 1.92 introduced a new signature requiring an explicit string ID to avoid widget ID collisions:

// Correct (new API):
if (ImGui::ImageButton("btn_save", tex_id, ImVec2(64, 64)))
    SaveCurrentState();

// Avoid the old overload that used tex_id as the widget ID

Low-Level Drawing with ImDrawList

For full control over UV coordinates, tint colors, and rounding, use ImDrawList::AddImage():

ImDrawList* draw = ImGui::GetWindowDrawList();
ImVec2 pos = ImGui::GetCursorScreenPos();
ImVec2 size = ImVec2(256, 256);
ImVec2 uv0 = ImVec2(0.0f, 0.0f);          // Top-left
ImVec2 uv1 = ImVec2(1.0f, 1.0f);          // Bottom-right

draw->AddImage(tex_id, pos, ImVec2(pos.x + size.x, pos.y + size.y), uv0, uv1);
ImGui::Dummy(size);                       // Advance layout cursor

Customizing ImTextureID for Engine Integration

If your engine requires additional metadata beyond a raw pointer, you can redefine ImTextureID in imconfig.h before including ImGui headers:

// imconfig.h
#define ImTextureID MyEngineTexture*

Where MyEngineTexture might be:

struct MyEngineTexture {
    GLuint glTex;           // Native OpenGL handle
    // ... extra metadata like sampler state ...
};

After this change, all ImGui functions accept MyEngineTexture* directly, implicitly converting to ImTextureRef when needed. Ensure your type can be safely cast to/from a 64-bit value for internal storage.

Common Pitfalls and Solutions

Pitfall Cause Solution
Wrong cast size Casting GLuint to void* on 32-bit builds truncates the value since ImTextureID is ImU64. Always cast through intptr_t: (ImTextureID)(intptr_t)native_handle
ImageButton ID collisions The old API used ImTextureID as the widget ID, causing collisions when using the same texture multiple times. Use the new signature with explicit string ID: ImGui::ImageButton("unique_id", tex_id, size)
Uninitialized texture state Forgetting to mark texture status as ready before drawing. Backend must call ImTextureData::SetTexID() and set status to OK after GPU upload
Type confusion Mixing raw ImTextureID with ImTextureRef in v1.92+ Either pass ImTextureRef explicitly or rely on implicit conversion from ImTextureID

Summary

  • ImTextureID is defined in [imgui.h](https://github.com/ocornut/imgui/blob/master/imgui.h#L3220-L3230) as a 64-bit opaque handle (ImU64) that represents any backend texture.
  • Convert native handles (e.g., ID3D11ShaderResourceView*, GLuint) by casting through intptr_t: (ImTextureID)(intptr_t)handle.
  • In v1.92+, textures are wrapped in ImTextureRef before storage in ImDrawCmd, though the API accepts raw ImTextureID via implicit conversion.
  • Render textures using ImGui::Image(), ImGui::ImageButton("id", tex, size), or ImDrawList::AddImage().
  • Customize the type by redefining ImTextureID in imconfig.h for engine-specific texture structs.

Frequently Asked Questions

What exactly is ImTextureID?

ImTextureID is a 64-bit unsigned integer typedef that serves as an opaque handle to backend-specific texture data. Dear ImGui does not know about DirectX, OpenGL, or Vulkan textures directly; instead, each rendering backend converts its native texture handle (like a GLuint or pointer) into this 64-bit value and passes it to ImGui's draw command structure.

Why does ImGui::ImageButton require a string ID now?

The older overload used the ImTextureID value itself as the widget identifier, which caused ID collisions when the same texture appeared multiple times in the UI. The new API requires an explicit string ID (e.g., ImGui::ImageButton("save_icon", tex_id, size)) to ensure unique widget identification and proper input handling.

Can I use a custom struct pointer instead of the default ImTextureID?

Yes, by redefining ImTextureID in imconfig.h before including ImGui headers. You can set #define ImTextureID MyEngineTexture* to pass your engine's texture objects directly, provided sizeof(MyEngineTexture*) fits within the 64-bit storage and can be safely cast back when the backend processes draw commands.

Do I need to manually manage ImTextureData when using custom textures?

Generally no—your rendering backend handles the ImTextureData lifecycle. However, you must ensure the backend calls ImTextureData::SetTexID() with your ImTextureID value and updates the texture status to OK after successfully uploading the texture to the GPU. ImGui asserts on this state during [imgui_draw.cpp](https://github.com/ocornut/imgui/blob/master/imgui_draw.cpp#L2670-L2870) rendering to prevent drawing with invalid textures.

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 →