# ImGui Tables and Grids Features: Complete API and Implementation Guide

> Master ImGui tables and grids with this complete API and implementation guide. Learn to use sorting, resizing, freezing, and clipping for high-performance data display.

- Repository: [omar/imgui](https://github.com/ocornut/imgui)
- Tags: deep-dive
- Published: 2026-07-19

---

**Dear ImGui's table system provides high-performance data grids with built-in sorting, resizing, column freezing, and clipping through a BeginTable/EndTable API that supports thousands of rows via ImGuiListClipper.**

The `ocornut/imgui` repository implements a sophisticated table and grid system in [`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp) that handles complex layout policies, interactive headers, and optimized rendering for large datasets. This article examines the actual source implementation to explain how ImGui tables manage column sizing, scrolling, and state persistence while maintaining interactive frame rates.

## Table Lifecycle and Core API

The table system follows a strict lifecycle defined in [`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp). Understanding this flow is essential for correctly implementing data grids.

### Initialization and Setup

A table begins with `ImGui::BeginTable`, which internally calls `BeginTableEx` (lines 14-22). This function creates a table instance, determines whether a child window is required for scrolling, and initializes the table flags.

Column registration happens via `TableSetupColumn` (lines 62-65), where you specify width policies and behavior flags:

```cpp
ImGui::BeginTable("MyTable", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders);
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 50.0f);
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 80.0f);

```

### Layout Computation

The first call to `TableNextRow` or `TableHeadersRow` triggers `TableUpdateLayout` (lines 350-380). This critical function computes:

- Final column widths based on sizing policies
- Display order and clipping rectangles
- Draw channel assignments

### Row Submission

Data population occurs through `TableNextRow` and `TableNextColumn` (lines 640-670). The API automatically accumulates row height from the tallest cell. `TableNextColumn` returns a boolean indicating whether the column is visible, allowing you to skip expensive widget creation for clipped columns.

The table finalizes with `EndTable` (lines 900-940), which merges draw channels, draws borders, and performs garbage collection.

## Column Sizing Policies and Layout Control

ImGui tables support two orthogonal sizing concepts controlled through `TableSetupColumn` flags and the `TableFixFlags` helper (lines 75-85).

### Width Policies

**WidthFixed**: The column uses a specific pixel width. If you specify `0.0f`, the width automatically fits the content.

**WidthStretch**: The column receives a weight value and occupies a proportional share of remaining horizontal space.

You can mix fixed and stretch columns in the same table. A common pattern uses fixed widths for leading columns (like IDs) and stretch policies for trailing descriptive columns to fill remaining space.

### Interaction Flags

- **NoResize**: Prevents users from dragging column boundaries
- **NoReorder**: Disables drag-and-drop column reordering

When horizontal scrolling is active, you must supply a positive `inner_width` parameter to give stretch columns a meaningful reference size (see comments around lines 15-20).

## Scrolling, Freezing, and Performance Optimization

ImGui tables handle large datasets through sophisticated clipping mechanisms and optional scrolling behaviors.

### Scrolling and Freezing

The `ImGuiTableFlags_ScrollX` and `ImGuiTableFlags_ScrollY` flags create an internal child window for scrolling. Combine these with freezing flags to keep headers and key columns visible:

```cpp
ImGuiTableFlags flags = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY |
                        ImGuiTableFlags_FreezeTopRow | 
                        ImGuiTableFlags_FreezeLeftColumn;

```

### Clipping and Culling

For tables with thousands of rows, use `ImGuiListClipper` (demonstrated in the "Tables → Vertical Scrolling" demo) to submit only visible rows. This technique intersects row rectangles with the clip rect (section "Tables Clipping/Culling", lines 158-165).

Additionally, `TableNextColumn` returns `false` when a column is fully clipped (lines 66-74), allowing you to skip rendering for hidden columns entirely. The implementation distinguishes three states: visible, clipped, and hidden (lines 166-180).

## Sorting and Header Interactions

When `ImGuiTableFlags_Sortable` is enabled, ImGui manages sort state automatically through the `TableSortSpecsClickColumn` function (lines 58-60).

### Sort Specifications

Clicking a header triggers the sort spec update, which populates the internal `ImGuiTableSortSpecs` array. Retrieve the current specs via `TableGetSortSpecs` to perform custom sorting in your application code:

```cpp
ImGuiTableSortSpecs* sorts = ImGui::TableGetSortSpecs();
if (sorts && sorts->SpecsDirty) {
    // Apply sorts->Specs[0].ColumnIndex and sorts->Specs[0].SortDirection
    // to your data source here
    sorts->SpecsDirty = false;
}

```

### Context Menus

The default header context menu (accessible via right-click) is built by `TableDrawDefaultContextMenu` (lines 55-56). Override this by calling `TableOpenContextMenu` before `TableHeadersRow` if you need custom menu items.

## Drawing the Grid and Borders

Border rendering occurs in `TableDrawBorders` (lines 710-770) using `ImDrawListSplitter` to manage three distinct draw channels:

1. **Channel 0**: Background and row strips
2. **Channel 1**: Frozen column overlays when `TableSetupScrollFreeze` is active
3. **Channel 2**: No-clip overlays when `ImGuiTableFlags_NoClip` is used

Border colors derive from the current style (`ImGuiCol_TableBorderStrong` and `ImGuiCol_TableBorderLight`) initialized during table setup (lines 446-448).

## State Persistence

Tables automatically persist user modifications to the `.ini` file unless you specify `ImGuiTableFlags_NoSavedSettings`. The functions `TableLoadSettings` and `TableSaveSettings` (called from `BeginTableEx` and `EndTable`) handle serialization of:

- Column order and visibility
- User-resized widths
- Sort specifications

## Complete Working Example

This implementation demonstrates fixed and stretch columns, sorting, freezing, and vertical clipping for large datasets:

```cpp
// Create table with scrolling, sorting, and resize capabilities
if (ImGui::BeginTable("DemoTable", 5,
    ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
    ImGuiTableFlags_Sortable | ImGuiTableFlags_ScrollX | 
    ImGuiTableFlags_ScrollY | ImGuiTableFlags_FreezeTopRow |
    ImGuiTableFlags_FreezeLeftColumn))
{
    // Configure columns: Fixed ID, stretching Name, fixed Value, auto Status, stretching Remarks
    ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 50.0f);
    ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_DefaultSort, 0.0f);
    ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 80.0f);
    ImGui::TableSetupColumn("Status", 0, 0.0f);
    ImGui::TableSetupColumn("Remarks", ImGuiTableColumnFlags_WidthStretch, 0.0f);
    ImGui::TableHeadersRow(); // Draws headers and enables sorting

    // Handle sorting logic
    ImGuiTableSortSpecs* sorts = ImGui::TableGetSortSpecs();
    if (sorts && sorts->SpecsDirty) {
        // Sort your data based on sorts->Specs array
        sorts->SpecsDirty = false;
    }

    // Clipper for handling 1000+ rows efficiently
    ImGuiListClipper clipper;
    clipper.Begin(1000);
    while (clipper.Step()) {
        for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++) {
            ImGui::TableNextRow();
            
            // Column 0: ID
            ImGui::TableNextColumn();
            ImGui::Text("%d", row);
            
            // Column 1: Name (skip if clipped)
            if (ImGui::TableNextColumn()) {
                ImGui::Text("Item %03d", row);
            }
            
            // Column 2: Interactive widget
            ImGui::TableNextColumn();
            ImGui::SliderFloat("##val", &values[row], 0.0f, 1.0f);
            
            // Column 3: Status
            ImGui::TableNextColumn();
            ImGui::Text("%s", (row % 2) ? "OK" : "FAIL");
            
            // Column 4: Remarks
            ImGui::TableNextColumn();
            ImGui::TextUnformatted("Lorem ipsum dolor sit amet");
        }
    }
    ImGui::EndTable();
}

```

## Summary

- **Core API**: Tables use `BeginTable`/`EndTable` with `TableSetupColumn` for configuration, processed by `TableUpdateLayout` before the first row submission.
- **Sizing**: Mix `WidthFixed` and `WidthStretch` policies to create responsive layouts; provide `inner_width` when horizontal scrolling is enabled.
- **Performance**: Utilize `ImGuiListClipper` for vertical scrolling with many rows, and check `TableNextColumn` return values to skip hidden columns.
- **Interactivity**: Enable `Sortable`, `Resizable`, and `Reorderable` flags for full UI control; access sort specs via `TableGetSortSpecs`.
- **Rendering**: Borders draw via three-channel splitters in `TableDrawBorders`, with colors defined in `ImGuiCol_TableBorderStrong` and `ImGuiCol_TableBorderLight`.
- **Persistence**: Column state automatically saves to `.ini` files via `TableSaveSettings` unless `NoSavedSettings` is specified.

## Frequently Asked Questions

### How do I enable horizontal scrolling in ImGui tables?

Specify the `ImGuiTableFlags_ScrollX` flag in `BeginTable`. When using horizontal scrolling with stretch columns, you must provide a positive `inner_width` parameter to give the stretch algorithm a reference size. This creates a child window internally that manages the scrollable region.

### What is the difference between WidthFixed and WidthStretch columns?

`WidthFixed` columns maintain a constant pixel width (or auto-fit if set to `0.0f`), while `WidthStretch` columns dynamically share remaining horizontal space proportionally. Fixed columns are ideal for data like IDs or dates that require consistent width, whereas stretch columns work best for content that should expand to fill available space, such as descriptions or comments.

### How does sorting work in ImGui tables?

When `ImGuiTableFlags_Sortable` is enabled, clicking column headers triggers `TableSortSpecsClickColumn`, which populates an `ImGuiTableSortSpecs` structure. Call `TableGetSortSpecs` to retrieve the current sort criteria, apply the sorting to your data array when `SpecsDirty` is true, then set `SpecsDirty` to false. The table does not sort your data automatically; it only tracks which columns the user wants sorted and in which direction.

### How do I optimize performance for tables with thousands of rows?

Use `ImGuiListClipper` to render only visible rows (as shown in [`imgui_demo.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_demo.cpp)). Additionally, check the boolean return value of `TableNextColumn`—it returns `false` for fully clipped columns, allowing you to skip expensive widget creation for off-screen cells. These techniques, combined with the internal clipping rectangles calculated in `TableUpdateLayout`, ensure smooth frame rates even with large datasets.