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

> Learn how to map and use ImTextureID to render custom images in Dear ImGui. Understand texture handles and ImGui draw commands for seamless integration.

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

---

**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/main/imgui.h)](https://github.com/ocornut/imgui/blob/master/imgui.h#L3220-L3230), `ImTextureID` is defined as a 64-bit unsigned integer by default:

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

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

```cpp
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`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp) lines 1999-2002](https://github.com/ocornut/imgui/blob/master/imgui_demo.cpp#L1999-L2002) demonstrates this with the font atlas:

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

```cpp
// 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()`:

```cpp
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`](https://github.com/ocornut/imgui/blob/main/imconfig.h)** before including ImGui headers:

```cpp
// imconfig.h
#define ImTextureID MyEngineTexture*

```

Where `MyEngineTexture` might be:

```cpp
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/main/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`](https://github.com/ocornut/imgui/blob/main/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`](https://github.com/ocornut/imgui/blob/main/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/main/imgui_draw.cpp)](https://github.com/ocornut/imgui/blob/master/imgui_draw.cpp#L2670-L2870) rendering to prevent drawing with invalid textures.