# How to Use Dear ImGui’s Tables API (BeginTable/EndTable) for Complex Layouts

> Master Dear ImGui's tables API and its BeginTable/EndTable functions to build complex, sortable, and scrollable layouts. Upgrade from legacy Columns for enhanced UI flexibility.

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

---

**Dear ImGui’s tables API provides a modern, flexible grid system that replaces the legacy Columns API, allowing you to create sortable, scrollable, and nested layouts using `BeginTable()`, `TableSetupColumn()`, and `TableNextRow()` within the ocornut/imgui repository.**

The Dear ImGui tables API (BeginTable/EndTable) offers a robust replacement for the deprecated Columns system, enabling complex widget arrangements with frozen headers, horizontal scrolling, and per-cell formatting. According to the ocornut/imgui source code, this system is implemented across [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h), [`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp), and [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h), providing fine-grained control over layout sizing, sorting, and display. This guide covers the architectural workflow and practical implementation patterns for building sophisticated interface layouts.

## Core Architecture of the Dear ImGui Tables API

The table system centers on a strict lifecycle that creates an internal child window capable of independent scrolling and clipping. The implementation in [[`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp)](https://github.com/ocornut/imgui/blob/master/imgui_tables.cpp) handles the layout engine, while the public interface is declared in [[`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h)](https://github.com/ocornut/imgui/blob/master/imgui.h#L882-L909).

The standard workflow follows this sequence:

1. **Initialize** with `BeginTable()` – returns `false` if the table is clipped and should be skipped.
2. **Declare columns** using `TableSetupColumn()` with sizing flags (e.g., `ImGuiTableColumnFlags_WidthFixed`) before the first row.
3. **Freeze rows/columns** via `TableSetupScrollFreeze()` to keep headers visible during scrolling.
4. **Render headers** with `TableHeadersRow()` or manually with `TableNextRow()` and `TableHeader()`.
5. **Populate data** by calling `TableNextRow()` followed by `TableNextColumn()` for each cell.
6. **Finalize** with `EndTable()` (only if `BeginTable()` returned true).

Internal state structures such as `ImGuiTable` and per-column data are defined in [[`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h)](https://github.com/ocornut/imgui/blob/master/imgui_internal.h#L3068-L3177).

## Declaring Columns and Sizing Policies

Column definitions determine how space is distributed and how users can interact with the layout. Call `TableSetupColumn()` after `BeginTable()` but before any row operations:

- **`ImGuiTableColumnFlags_WidthFixed`**: Allocates absolute pixel width (default 100.0f unless overridden).
- **`ImGuiTableColumnFlags_WidthStretch`**: Distributes remaining space proportionally using weight values.
- **`ImGuiTableColumnFlags_NoResize`**: Prevents manual resizing when combined with `ImGuiTableFlags_Resizable`.
- **`ImGuiTableColumnFlags_DefaultSort`**: Marks the column as the default sort key when `ImGuiTableFlags_Sortable` is enabled.

The initial width or weight parameter in `TableSetupColumn()` interacts with the `inner_width` parameter of `BeginTable()` to determine the content region’s virtual size.

## Implementing Scrolling and Frozen Headers

For tables that exceed their container bounds, horizontal scrolling is enabled by passing `ImGuiTableFlags_ScrollX` to `BeginTable()`:

```cpp
ImGui::BeginTable("Scrollable", 4, 
    ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollX, 
    ImVec2(0.0f, 0.0f),  // outer_size
    0.0f);                // inner_width (0 = auto)

```

To freeze header rows or identifier columns, use `TableSetupScrollFreeze(freeze_columns, freeze_rows)` immediately after declaring columns. This keeps the specified rows and columns visible while the remainder of the table scrolls, implemented in the layout engine at [[`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp)](https://github.com/ocornut/imgui/blob/master/imgui_tables.cpp).

## Row and Column Iteration Patterns

Populate tables by alternating between row and column advancement:

- **`TableNextRow()`**: Advances to a new row, optionally specifying row flags and minimum height.
- **`TableNextColumn()`**: Advances to the next column, automatically wrapping to the next row after the final column is reached.
- **`TableSetColumnIndex(int column_n)`**: Jumps to a specific column index, useful for random access or spanning cells.

Critical rule: The first call to `TableNextColumn()` must occur after `TableNextRow()` to establish the row context. Subsequent calls iterate through cells.

## Advanced Complex Layout Techniques

### Nested Tables

Tables can be nested because each `BeginTable()` creates an isolated child window. This allows complex hierarchical layouts where outer tables control global structure and inner tables manage detailed grids:

```cpp
if (ImGui::BeginTable("Outer", 2, ImGuiTableFlags_Borders)) {
    ImGui::TableSetupColumn("Left");
    ImGui::TableSetupColumn("Right");
    ImGui::TableHeadersRow();

    ImGui::TableNextRow();
    ImGui::TableNextColumn();
    ImGui::Text("Static Content");

    ImGui::TableNextColumn();
    if (ImGui::BeginTable("Inner", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
        ImGui::TableSetupColumn("A");
        ImGui::TableSetupColumn("B");
        ImGui::TableSetupColumn("C");
        ImGui::TableHeadersRow();

        for (int i = 0; i < 4; ++i) {
            ImGui::TableNextRow();
            ImGui::TableNextColumn(); ImGui::Text("R%dA", i);
            ImGui::TableNextColumn(); ImGui::Text("R%dB", i);
            ImGui::TableNextColumn(); ImGui::Text("R%dC", i);
        }
        ImGui::EndTable();
    }
    ImGui::EndTable();
}

```

### Column Spanning and Background Colors

To span a cell across multiple columns, use `TableSetColumnIndex()` to position the cursor, then skip subsequent `TableNextColumn()` calls for the spanned columns. Customize cell appearance with `TableSetBgColor()`:

```cpp
if (ImGui::BeginTable("SpanTable", 3, ImGuiTableFlags_Borders)) {
    ImGui::TableSetupColumn("Name");
    ImGui::TableSetupColumn("Description");
    ImGui::TableSetupColumn("Value");
    ImGui::TableHeadersRow();

    ImGui::TableNextRow();
    ImGui::TableNextColumn(); 
    ImGui::Text("Item A");
    
    // Span description across remaining columns
    ImGui::TableSetColumnIndex(1);
    ImGui::Text("This spans the description");
    // Skip TableNextColumn() for column 2 to complete the span
    
    // Highlight entire row background
    ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, IM_COL32(30, 30, 160, 255));
    ImGui::EndTable();
}

```

Utility functions like `TableSetColumnEnabled()` dynamically hide columns, and `TableGetSortSpecs()` retrieves current sorting criteria when `ImGuiTableFlags_Sortable` is active.

## Practical Implementation Examples

### Fixed-Width Table with Headers

```cpp
if (ImGui::BeginTable("SimpleTable", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
    ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed);
    ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
    ImGui::TableSetupColumn("Score", ImGuiTableColumnFlags_WidthFixed);
    ImGui::TableHeadersRow();

    for (int i = 0; i < 5; ++i) {
        ImGui::TableNextRow();
        ImGui::TableNextColumn(); ImGui::Text("%d", i);
        ImGui::TableNextColumn(); ImGui::Text("Player %d", i);
        ImGui::TableNextColumn(); ImGui::Text("%d", rand() % 100);
    }
    ImGui::EndTable();
}

```

### Stretch Columns with Horizontal Scrolling

```cpp
ImGui::BeginTable("StretchTable", 4,
    ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollX | ImGuiTableFlags_SizingStretchSame,
    ImVec2(0.0f, 0.0f), 0.0f);
{
    ImGui::TableSetupColumn("A");
    ImGui::TableSetupColumn("B");
    ImGui::TableSetupColumn("C");
    ImGui::TableSetupColumn("D");
    ImGui::TableHeadersRow();

    for (int row = 0; row < 10; ++row) {
        ImGui::TableNextRow();
        for (int col = 0; col < 4; ++col) {
            ImGui::TableNextColumn();
            ImGui::Text("R%dC%d", row, col);
        }
    }
}
ImGui::EndTable();

```

## Summary

- **BeginTable()** creates a scoped child window that must be paired with **EndTable()**, returning `false` when clipped.
- Declare column properties using **TableSetupColumn()** before the first row, utilizing `ImGuiTableColumnFlags_WidthFixed` or `ImGuiTableColumnFlags_WidthStretch` for precise sizing control.
- Iterate through the grid using **TableNextRow()** followed by **TableNextColumn()**, which automatically wraps after the last column.
- Enable **TableSetupScrollFreeze()** to keep headers visible during scrolling, and pass **ImGuiTableFlags_ScrollX** for horizontal overflow.
- Implement complex hierarchies with nested tables, column spanning via **TableSetColumnIndex()**, and visual customization with **TableSetBgColor()**.
- Reference the API declarations in [[`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h)](https://github.com/ocornut/imgui/blob/master/imgui.h#L882-L909), the layout engine in [[`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp)](https://github.com/ocornut/imgui/blob/master/imgui_tables.cpp), and internal structures in [[`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h)](https://github.com/ocornut/imgui/blob/master/imgui_internal.h#L3068-L3177).

## Frequently Asked Questions

### How do I enable horizontal scrolling in a Dear ImGui table?

Pass `ImGuiTableFlags_ScrollX` to `BeginTable()` and specify an `inner_width` parameter larger than the outer size or zero for automatic sizing. This creates a child window that clips and scrolls horizontally according to the implementation in [[`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp)](https://github.com/ocornut/imgui/blob/master/imgui_tables.cpp).

### Can I nest tables inside other tables?

Yes, calling `BeginTable()` inside a cell creates an independent child window with its own scroll and layout context. This allows complex hierarchical designs where outer tables manage structural layout and inner tables present detailed data grids.

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

**WidthFixed** allocates a specific pixel width defined in `TableSetupColumn()`, while **WidthStretch** distributes remaining host window space proportionally based on weight values. Mixing both types allows fixed-width identifiers alongside fluid content columns.

### How do I freeze header rows so they remain visible while scrolling?

Call `TableSetupScrollFreeze(columns, rows)` immediately after declaring columns, typically with `TableSetupScrollFreeze(0, 1)` to freeze the first row. This modifies the clipping region in the table layout engine to keep frozen sections visible during vertical or horizontal scroll operations.