# Using ImGuiListClipper for Efficient Rendering of Large Lists in Dear ImGui

> Learn how to use ImGuiListClipper to efficiently render massive lists in Dear ImGui. Optimize performance by rendering only visible items and avoid costly CPU usage.

- Repository: [omar/imgui](https://github.com/ocornut/imgui)
- Tags: performance
- Published: 2026-07-14

---

**`ImGuiListClipper` is a virtualization helper that renders only the visible subset of large lists, preventing expensive CPU overhead when displaying thousands of items.**

The `ocornut/imgui` repository provides this specialized class in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) to solve performance bottlenecks in long scrollable regions. By calculating which rows intersect the current clipping rectangle, your application skips processing for off-screen items entirely.

## How ImGuiListClipper Works Internally

Understanding the internal mechanics helps you debug edge cases and optimize integration with custom widgets.

### State Allocation and Temporary Buffers

When you call `Begin()`, the clipper obtains transient storage from `ImGuiContext::ClipperTempData` at lines [3264‑3267](https://github.com/ocornut/imgui/blob/master/imgui.cpp#L3264-L3267). This buffer holds the clipping ranges, step counters, and seek offsets needed for the current frame. The system automatically returns this memory when the clipper goes out of scope, avoiding heap allocations during the render loop.

### Height Measurement and Range Calculation

If you omit the `itemHeight` parameter in `Begin()`, the clipper forces measurement of the first item at lines [3328‑3336](https://github.com/ocornut/imgui/blob/master/imgui.cpp#L3328-L3336) to determine `DisplayStart` and `DisplayEnd`. Once the height is known, `ImGuiListClipper_SortAndFuseRanges` (lines [3269‑3285](https://github.com/ocornut/imgui/blob/master/imgui.cpp#L3269-L3285)) merges overlapping visible blocks into coherent `ImGuiListClipperRange` structures defined in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) at lines [1889‑1897](https://github.com/ocornut/imgui/blob/master/imgui_internal.h#L1889-L1897). Each range stores `Min` and `Max` indices representing contiguous visible segments.

### Cursor Seeking and Iterative Stepping

Between visible blocks, `ImGuiListClipper_SeekCursorAndSetupPrevLine` (lines [3288‑3302](https://github.com/ocornut/imgui/blob/master/imgui.cpp#L3288-L3302)) advances the ImGui cursor to the next visible region while preserving window state for scrolling and navigation. The public `Step()` method at lines [3562‑3566](https://github.com/ocornut/imgui/blob/master/imgui.cpp#L3562-L3566) wraps the internal `ImGuiListClipper_StepInternal` function, returning `true` while additional ranges remain. Finally, `End()` (lines [3545‑3550](https://github.com/ocornut/imgui/blob/master/imgui.cpp#L3545-L3550)) positions the cursor after the last logical item to ensure subsequent UI elements layout correctly.

## When to Use ImGuiListClipper

Implement the clipper when rendering lists that exceed the practical limits of immediate-mode submission:

- **Large static datasets** – Lists containing 10,000+ entries with uniform row heights
- **Dynamic collections** – Data streams where you can provide a constant `itemHeight` to `Begin(itemsCount, itemHeight)`
- **Complex containers** – Tables (see the `FIXME‑TABLE` comment at line [3256](https://github.com/ocornut/imgui/blob/master/imgui.cpp#L3256)) and tree views that require scrolling performance
- **Multi-select interfaces** – Combining clipper iteration with `ImGuiMultiSelectIO` to maintain selection state across virtualization boundaries

## Implementation Examples

### Basic List Virtualization

This minimal example demonstrates the standard pattern for virtualizing a 20,000-item list:

```cpp
int ItemsCount = 20000;
ImGuiListClipper clipper;
clipper.Begin(ItemsCount);  // Optional: clipper.Begin(ItemsCount, itemHeight);

while (clipper.Step())
{
    for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i)
    {
        // Only visible items are submitted to ImGui
        ImGui::Text("Item %d", i);
    }
}

```

### Integration with Multi-Select APIs

When combining with `BeginMultiSelect`, use `IncludeItemByIndex()` to ensure selection anchors remain visible:

```cpp
int ItemsCount = 10000;
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape, 0, ItemsCount);

ImGuiListClipper clipper;
clipper.Begin(ItemsCount);

if (ms_io->RangeSrcItem != -1)
    clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem);

while (clipper.Step())
{
    for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; ++n)
    {
        char label[64];
        sprintf(label, "Object %05d", n);
        bool selected = /* your selection state */;
        
        ImGui::SetNextItemSelectionUserData(n);
        ImGui::Selectable(label, selected);
    }
}
ImGui::EndMultiSelect();

```

### Clipping Inside Tables

The clipper functions correctly within table structures, as shown in [`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp) at lines [3110‑3128](https://github.com/ocornut/imgui/blob/master/imgui_demo.cpp#L3110-L3128):

```cpp
if (ImGui::BeginTable("tbl", 2, ImGuiTableFlags_ScrollY))
{
    ImGuiListClipper clipper;
    clipper.Begin(ItemsCount);
    
    while (clipper.Step())
    {
        for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; ++n)
        {
            ImGui::TableNextRow();
            ImGui::TableNextColumn();
            ImGui::Text("Row %d", n);
            ImGui::TableNextColumn();
            ImGui::Text("Data %d", n);
        }
    }
    ImGui::EndTable();
}

```

## Summary

- **`ImGuiListClipper`** virtualizes large lists by calculating visible ranges in `ImGuiContext::ClipperTempData` and only submitting those items to the draw pipeline.
- **Height measurement** occurs automatically on the first step if you omit the `itemHeight` parameter in `Begin()`.
- **Range fusion** via `ImGuiListClipper_SortAndFuseRanges` handles fragmented visibility areas, while `ImGuiListClipper_SeekCursorAndSetupPrevLine` maintains correct cursor positioning.
- **Integration patterns** include standalone lists, table rows, and multi-select operations using `IncludeItemByIndex()` for anchor preservation.
- **Source references** are located in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) (implementation), [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) (range structures), and [`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp) (usage examples).

## Frequently Asked Questions

### What is the minimum number of items to justify using ImGuiListClipper?

While the clipper functions correctly with any list size, it provides measurable performance benefits once you exceed **1,000 to 2,000 items** depending on widget complexity. For fewer than 500 simple text rows, the overhead of range calculation may outweigh the savings, though the clipper remains safe to use.

### Can ImGuiListClipper handle variable row heights?

No. The current implementation assumes uniform item heights to compute visibility ranges mathematically without measuring every row. If you pass a known height to `Begin()`, that value applies to all items. Variable heights require custom virtualization logic or manual calculation of `DisplayStart`/`DisplayEnd` outside the standard clipper API.

### How does ImGuiListClipper interact with keyboard navigation?

The clipper preserves navigation state through `ImGuiListClipper_SeekCursorAndSetupPrevLine`, which updates window scrolling and focus regions. When using multi-select, call `IncludeItemByIndex()` on the `RangeSrcItem` to ensure the anchor item is submitted even if it falls outside the visible clipping rectangle, maintaining selection continuity.

### Is ImGuiListClipper compatible with ImGui::BeginTable?

Yes. The clipper works inside tables using `ImGuiTableFlags_ScrollY`, as demonstrated in [`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp). The clipper iterates over row indices, and you call `TableNextRow()` within the `Step()` loop. A `FIXME‑TABLE` comment at line [3256](https://github.com/ocornut/imgui/blob/master/imgui.cpp#L3256) indicates ongoing refinements for complex table features, but the core virtualization is fully supported.