# How to Effectively Use the Dear ImGui Table API for Complex UI Layouts

> Master the Dear ImGui table API for complex UI layouts. Learn to build scrollable, resizable grids with flexible sizing and explicit lifecycle functions. Elevate your UI design today.

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

---

**The Dear ImGui table API replaces the deprecated Columns API with explicit lifecycle functions and flexible sizing policies defined in [`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp), enabling developers to build scrollable, resizable, and data-intensive UI grids.**

The **Dear ImGui table API**, found in the `ocornut/imgui` repository, provides a modern solution for rendering complex tabular data with precise layout control. Unlike the legacy Columns API, this system uses explicit state management through functions declared in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) and implemented in [`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp), offering superior performance for large datasets and supporting advanced features like frozen headers and programmatic column manipulation. Mastering this API requires understanding the strict initialization sequence, sizing policies, and interaction helpers that govern table behavior.

## Understanding the Table Lifecycle

Every table follows a strict initialization and rendering sequence managed by five core functions. **In [`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp), the `BeginTable` function (lines 315-322)** allocates an internal `ImGuiTable` structure and determines the sizing policy based on provided flags. You must declare columns using **`TableSetupColumn`** (lines 1737-1744) before submitting any row data, as this function defines width constraints, visibility flags, and optional user IDs that persist across frames.

After configuration, **`TableNextRow`** signals the start of a new row and triggers layout calculations via the internal `TableUpdateLayout` routine. Within each row, **`TableNextColumn`** advances the cursor to the next cell, returning `false` when the row is complete. Finally, **`EndTable`** (lines 1463-1504) validates the `BeginTable`/`EndTable` pairing and releases temporary buffers. Omitting `EndTable` results in immediate assertion failures, making proper lifecycle management critical for application stability.

## Configuring Table Sizing Policies

The table's overall dimensions are governed by **`ImGuiTableFlags_Sizing*`** flags processed during initialization. **In [`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp) around lines 278-283**, ImGui automatically selects a default policy based on scroll settings if no explicit flag is provided. The three primary policies offer distinct layout behaviors:

- **`SizingFixedFit`** – Columns default to `WidthFixed`, with each column sized to fit its content width exactly.
- **`SizingFixedSame`** – All fixed columns share the width of the widest content column, creating uniform fixed widths.
- **`SizingStretchSame`** – Columns default to `WidthStretch` with equal weight distribution, dividing available horizontal space evenly between columns.

For complex layouts, combine these flags with explicit column definitions. Tables without `ImGuiTableFlags_ScrollX` typically default to stretch policies, while scrolling tables often use fixed policies to maintain content alignment.

## Fine-Tuning Column Behavior

Individual column characteristics are controlled through **`ImGuiTableColumnFlags`** passed to `TableSetupColumn`. **Around line 789 of [`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp), the `TableSetupColumnFlags` function** processes these flags, while `TableSetupColumnApply` (line 1710) applies them to the internal column structure.

Critical flags for complex UIs include:

- **`WidthFixed`** and **`WidthStretch`** – Force a column to maintain a specific pixel width or expand proportionally based on its weight.
- **`NoHide`** – Prevents users from hiding the column via the context menu.
- **`NoHeaderLabel`** – Hides the header text while preserving the resizable column area.
- **`DefaultHide`** – Hides the column initially, useful for optional data fields that users can toggle visible.

These flags must be set every frame before the first `TableNextRow` call, as column configurations are not automatically persisted between frames unless managed through ImGui's settings system.

## Implementing Scrolling and Frozen Headers

When you enable **`ImGuiTableFlags_ScrollX`** or **`ImGuiTableFlags_ScrollY`**, ImGui creates an internal child window for the table body to enable early clipping optimizations. This behavior is guarded by the `use_child_window` check around line 340 in [`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp). The **`TableSetupScrollFreeze(cols, rows)`** function locks specific columns and rows in place during scrolling, essential for maintaining header visibility or index columns in large datasets.

Control whether the table extends to fill available space using **`NoHostExtendX`** and **`NoHostExtendY`**, which determine if the outer table dimensions strictly match content or expand to fill the available region. Frozen columns remain interactive and resizable while the rest of the table scrolls beneath them.

## Practical Implementation Example

The following pattern demonstrates a complex table mixing fixed-width identification columns with stretchable content areas, scrollable body, and interactive elements:

```cpp
// Begin table with 5 columns, resizable borders, and vertical scrolling
if (ImGui::BeginTable("MyComplexTable", 5,
    ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY,
    ImVec2(0.0f, ImGui::GetFontSize() * 12)))  // Fixed outer height
{
    // Define column layout: mixed fixed and stretch widths
    ImGui::TableSetupColumn("ID",       ImGuiTableColumnFlags_WidthFixed,   60.0f);
    ImGui::TableSetupColumn("Name",     ImGuiTableColumnFlags_WidthStretch, 0.0f);
    ImGui::TableSetupColumn("Status",   ImGuiTableColumnFlags_WidthFixed,   80.0f);
    ImGui::TableSetupColumn("Progress", ImGuiTableColumnFlags_WidthStretch, 0.0f);
    ImGui::TableSetupColumn("Actions",  ImGuiTableColumnFlags_WidthFixed,   120.0f);

    // Render header row using names defined above
    ImGui::TableHeadersRow();

    // Populate data rows
    for (int n = 0; n < items_count; n++)
    {
        ImGui::TableNextRow();
        
        // Column 0: ID
        ImGui::TableNextColumn();
        ImGui::Text("%d", n);
        
        // Column 1: Name
        ImGui::TableNextColumn();
        ImGui::Text("%s", items[n].name.c_str());
        
        // Column 2: Status with color coding
        ImGui::TableNextColumn();
        ImVec4 color = items[n].active ? ImVec4(0,1,0,1) : ImVec4(1,0,0,1);
        ImGui::TextColored(color, items[n].active ? "Active" : "Idle");
        
        // Column 3: Progress bar
        ImGui::TableNextColumn();
        ImGui::ProgressBar(items[n].progress);
        
        // Column 4: Action buttons
        ImGui::TableNextColumn();
        if (ImGui::Button(("Edit##"+std::to_string(n)).c_str()))
            EditItem(n);
        ImGui::SameLine();
        if (ImGui::Button(("Del##"+std::to_string(n)).c_str()))
            DeleteItem(n);
    }
    ImGui::EndTable();
}

```

This implementation leverages the **`TableHeadersRow`** helper to generate headers from `TableSetupColumn` definitions and uses unique ID suffixes (`##ID`) to distinguish buttons across rows. The combination of fixed widths for compact data (IDs, actions) and stretch columns for variable content (names, progress bars) creates a responsive layout that maintains usability across window resizes.

## Summary

- **The Dear ImGui table API** provides explicit lifecycle management through `BeginTable`, `TableSetupColumn`, and `EndTable`, replacing the implicit state of the legacy Columns API.
- **Sizing policies** (`SizingFixedFit`, `SizingStretchSame`) are determined by flags processed in `imgui_tables.cpp:278-283` and control how columns distribute available horizontal space.
- **Column flags** like `WidthFixed`, `NoHide`, and `DefaultHide` must be declared every frame before the first row submission to configure visibility and resize behavior.
- **Scrolled tables** create internal child windows for clipping optimization, while `TableSetupScrollFreeze` locks headers and index columns during navigation.
- **Source files** [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) (public API) and [`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp) (implementation) contain the authoritative definitions for all table functionality.

## Frequently Asked Questions

### What is the difference between the Dear ImGui Table API and the old Columns API?

The Dear ImGui table API uses an explicit lifecycle with `BeginTable`/`EndTable` pairs and state stored in dedicated `ImGuiTable` structures defined in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h), whereas the old Columns API relied on implicit global state and offered limited customization. The new API supports independent column sizing policies, freezing, and context menus for column visibility, making it suitable for complex data grids that the Columns API could not handle efficiently.

### How do I make table columns resizable by users?

Pass **`ImGuiTableFlags_Resizable`** to `BeginTable`. This flag enables interactive resizing via the column header borders. You can programmatically adjust widths using **`TableSetColumnWidth(col_idx, width)`** after the table has begun, though user interactions will override these values during the same frame unless locked via specific column flags.

### How can I freeze the first row or column while scrolling?

Call **`TableSetupScrollFreeze(cols, rows)`** immediately after `BeginTable` and before `TableHeadersRow`. The first parameter specifies how many columns remain fixed on the left, while the second parameter locks rows at the top. This function modifies the internal clip rect calculations in [`imgui_tables.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_tables.cpp) to render frozen elements above the scrolled content.

### Why must I call TableSetupColumn every frame?

Unlike some UI systems that cache column definitions, Dear ImGui rebuilds table state each frame to support dynamic layout changes. **In `imgui_tables.cpp:1737-1744`**, `TableSetupColumn` validates flags and recalculates column widths based on current content and available space. This approach allows runtime changes to column count, ordering, or visibility without persistent state management, though it requires consistent declaration in your render loop.