# How to Use ImGuiListClipper for Efficient List Rendering in Dear ImGui

> Master ImGuiListClipper to render only visible items in Dear ImGui. Drastically cut iteration costs for large lists by optimizing performance with this essential technique.

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

---

**ImGuiListClipper is a helper class that renders only visible items in scrolling regions, reducing O(N) iteration costs to O(V) for visible rows only.**

The `ImGuiListClipper` class in the [ocornut/imgui](https://github.com/ocornut/imgui) repository solves the performance bottleneck of rendering large datasets by skipping invisible items entirely. When displaying thousands or millions of rows, iterating through every entry to call ImGui functions creates unnecessary CPU overhead. This clipper leverages internal visibility calculations to process only the subset of items that intersect the current viewport.

## Core Architecture and Source Files

The public API resides in **[imgui.h](https://github.com/ocornut/imgui/blob/master/imgui.h)** at lines 2894–2921, where the `ImGuiListClipper` struct declares the constructor, `Begin()`, `Step()`, `End()` methods, and the `DisplayStart`/`DisplayEnd` indices. Internal bookkeeping structures live in **[imgui_internal.h](https://github.com/ocornut/imgui/blob/master/imgui_internal.h)**:

- `ImGuiListClipperRange` (lines 1689–1699): Stores clipping ranges and seek offsets
- `ImGuiListClipperData` (lines 1702–1712): Maintains step counters and range buffers

The algorithm implementation appears in **[imgui.cpp](https://github.com/ocornut/imgui/blob/master/imgui.cpp)**, specifically within the `ImGuiListClipper::Begin`, `Step`, and `End` method definitions.

## How ImGuiListClipper Works Internally

The clipper operates through a three-phase state machine that caches visibility results across frames:

1. **Begin** – Initialize with `items_count` (total rows) and optional `items_height`. Pass `INT_MAX` if the count is unknown.
2. **Step** – Advance internal state and populate `DisplayStart` and `DisplayEnd` with the indices of rows intersecting the visible scrolling region. Returns `true` while work remains.
3. **End** – Reset internal buffers, allowing clipper reuse. Called automatically by the destructor.

The clipper handles **variable item heights** by refining estimates after the first frame renders, and automatically synchronizes with table row counters unless `ImGuiListClipperFlags_NoSetTableRowCounters` is specified.

## Basic Usage Pattern

Wrap your rendering loop inside the clipper's `Step()` iteration:

```cpp
ImGuiListClipper clipper;
clipper.Begin(items_count);
while (clipper.Step())
{
    for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
    {
        // Heavy per-item work only executes for visible rows
        ImGui::Text("Item %d", i);
        ImGui::Text("Data: %s", large_dataset[i].label);
    }
}
clipper.End();  // Optional: destructor handles this

```

This pattern ensures expensive operations—string formatting, image loading, or complex widget calculations—execute only for approximately **V** visible rows rather than **N** total items.

## Advanced Configuration Scenarios

### Variable Item Heights

When rows have non-uniform heights, provide an average estimate to `Begin()`:

```cpp
// Second parameter is approximate height in pixels
clipper.Begin(items_count, 20.0f);

```

The clipper recalculates precise ranges after measuring actual rendered heights during the first frame.

### Unknown Item Counts

For streaming data or lazily-generated lists, pass `INT_MAX` and break when data ends:

```cpp
clipper.Begin(INT_MAX);
while (clipper.Step())
{
    for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
    {
        if (i >= live_data_buffer.size()) 
            break;
        ImGui::Text("Live item %d", i);
    }
}

```

### Table Integration

Inside `BeginTable` calls, the clipper automatically calls `TableNextRow()` for each visible index:

```cpp
if (ImGui::BeginTable("LargeTable", 3, ImGuiTableFlags_ScrollY))
{
    ImGuiListClipper clipper;
    clipper.Begin(row_count);
    while (clipper.Step())
    {
        for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++)
        {
            ImGui::TableNextRow();
            ImGui::TableSetColumnIndex(0);
            ImGui::Text("%d", row);
            ImGui::TableSetColumnIndex(1);
            ImGui::Text("%f", data[row].value);
            ImGui::TableSetColumnIndex(2);
            ImGui::Text("%s", data[row].name);
        }
    }
    ImGui::EndTable();
}

```

Disable automatic table row management by passing `ImGuiListClipperFlags_NoSetTableRowCounters` to `Begin()` if you need manual control over row advancement.

## Summary

- **ImGuiListClipper** lives in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) and eliminates invisible item processing in large lists
- **Begin(), Step(), End()** form the core API for driving the visibility algorithm
- **DisplayStart/DisplayEnd** provide the index range for your rendering loop
- **Variable heights** and **tables** receive automatic optimization with optional manual override flags
- **INT_MAX** support enables streaming data scenarios without predetermined counts

## Frequently Asked Questions

### What is ImGuiListClipper and why is it necessary?

ImGuiListClipper is a CPU optimization utility that calculates which rows of a scrolling list are actually visible within the current window clipping rectangle. Without it, iterating through thousands of items to call ImGui functions—even for off-screen rows—wastes significant CPU cycles on invisible geometry that gets discarded during rendering.

### How does ImGuiListClipper handle items with different heights?

The clipper accepts an estimated height parameter in `Begin()`. During the first frame, it uses this estimate to guess the visible range, then refines the calculation after ImGui measures actual item heights. Subsequent frames use the cached measurements for accurate positioning regardless of row-to-row height variation.

### Can ImGuiListClipper work with Dear ImGui tables?

Yes. When used inside `BeginTable`/`EndTable` blocks, the clipper automatically invokes `TableNextRow()` for each visible index, maintaining proper table row counters and clip rectangles. Use `ImGuiListClipperFlags_NoSetTableRowCounters` only if your code manually manages row advancement via `TableNextRow()`.

### What happens if I don't know the total number of items?

Pass `INT_MAX` as the item count to `Begin()`. The clipper will continue emitting steps until `Step()` returns `false` or you break the loop internally. This supports use cases like log tailing or infinite scroll where data arrives continuously and the total size is unknown.