Using ImGuiListClipper for Efficient Rendering of Large Lists in Dear ImGui
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 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. 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 to determine DisplayStart and DisplayEnd. Once the height is known, ImGuiListClipper_SortAndFuseRanges (lines 3269‑3285) merges overlapping visible blocks into coherent ImGuiListClipperRange structures defined in imgui_internal.h at lines 1889‑1897. Each range stores Min and Max indices representing contiguous visible segments.
Cursor Seeking and Iterative Stepping
Between visible blocks, ImGuiListClipper_SeekCursorAndSetupPrevLine (lines 3288‑3302) 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 wraps the internal ImGuiListClipper_StepInternal function, returning true while additional ranges remain. Finally, End() (lines 3545‑3550) 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
itemHeighttoBegin(itemsCount, itemHeight) - Complex containers – Tables (see the
FIXME‑TABLEcomment at line 3256) and tree views that require scrolling performance - Multi-select interfaces – Combining clipper iteration with
ImGuiMultiSelectIOto 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:
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:
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 at lines 3110‑3128:
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
ImGuiListClippervirtualizes large lists by calculating visible ranges inImGuiContext::ClipperTempDataand only submitting those items to the draw pipeline.- Height measurement occurs automatically on the first step if you omit the
itemHeightparameter inBegin(). - Range fusion via
ImGuiListClipper_SortAndFuseRangeshandles fragmented visibility areas, whileImGuiListClipper_SeekCursorAndSetupPrevLinemaintains 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(implementation),imgui_internal.h(range structures), andimgui_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. The clipper iterates over row indices, and you call TableNextRow() within the Step() loop. A FIXME‑TABLE comment at line 3256 indicates ongoing refinements for complex table features, but the core virtualization is fully supported.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →