Tables

DataTable displays tabular data backed by a ListState. Mutations to the list automatically update the UI.

Basic table

"""Basic table — ListState-driven data table."""

import libui
from libui.declarative import (
    App,
    Window,
    VBox,
    HBox,
    Label,
    Button,
    State,
    stretchy,
    DataTable,
    ListState,
    TextColumn,
    ProgressColumn,
)


async def main():
    app = App()
    status = State("Click a row.")

    data = ListState(
        [
            {"name": "Alice", "role": "Engineer", "score": 85},
            {"name": "Bob", "role": "Designer", "score": 72},
            {"name": "Carol", "role": "Manager", "score": 90},
        ]
    )

    counter = State(len(data))

    def add_row():
        counter.update(lambda n: n + 1)
        data.append(
            {
                "name": f"Person {counter.value}",
                "role": "New",
                "score": 50,
            }
        )

    app.build(
        window=Window(
            "Basic Table",
            500,
            350,
            child=VBox(
                Label(text=status),
                stretchy(
                    DataTable(
                        data,
                        TextColumn("Name", key="name"),
                        TextColumn("Role", key="role"),
                        ProgressColumn("Score", key="score"),
                        on_row_clicked=lambda row: status.set(
                            f"Clicked: {data[row]['name']}"
                        ),
                    )
                ),
                HBox(
                    Button("Add Row", on_clicked=add_row),
                    Button(
                        "Remove Last",
                        on_clicked=lambda: data.pop() if len(data) else None,
                    ),
                ),
            ),
        )
    )

    app.show()
    await app.wait()


libui.run(main())
Basic table

Each row is a dictionary. Column descriptors map dictionary keys to table columns:

  • TextColumn("Name", key="name") — displays row["name"] as text

  • ProgressColumn("Score", key="score") — displays row["score"] as a progress bar (0-100)

Editable table

Columns can be made editable so users can modify data in place:

"""Editable table — inline editing with CheckboxTextColumn."""

import libui
from libui.declarative import (
    App,
    Window,
    VBox,
    Label,
    State,
    stretchy,
    DataTable,
    ListState,
    TextColumn,
    CheckboxTextColumn,
)


async def main():
    app = App()
    status = State("Edit cells directly in the table.")

    data = ListState(
        [
            {"done": 1, "task": "Write documentation", "notes": "In progress"},
            {"done": 0, "task": "Fix bug #42", "notes": "Investigate crash"},
            {"done": 1, "task": "Review PR", "notes": "Approved"},
            {"done": 0, "task": "Deploy v2.0", "notes": "Waiting on tests"},
        ]
    )

    app.build(
        window=Window(
            "Editable Table",
            550,
            300,
            child=VBox(
                Label(text=status),
                stretchy(
                    DataTable(
                        data,
                        CheckboxTextColumn(
                            "Task",
                            checkbox_key="done",
                            text_key="task",
                            checkbox_editable=True,
                            text_editable=True,
                        ),
                        TextColumn("Notes", key="notes", editable=True),
                        on_row_clicked=lambda row: status.set(
                            f"Row {row}: {data[row]['task']} "
                            f"({'done' if data[row]['done'] else 'pending'})"
                        ),
                    )
                ),
            ),
        )
    )

    app.show()
    await app.wait()


libui.run(main())
Editable table

TextColumn(..., editable=True) allows inline text editing. CheckboxColumn and CheckboxTextColumn support editable checkboxes.

Button columns

ButtonColumn renders a clickable button in each row. The on_click callback receives the row index:

"""Table with button columns — clickable actions per row."""

import libui
from libui.declarative import (
    App,
    Window,
    VBox,
    Label,
    State,
    stretchy,
    DataTable,
    ListState,
    TextColumn,
    ProgressColumn,
    ButtonColumn,
)


async def main():
    app = App()
    status = State("Click a button in the table.")

    data = ListState(
        [
            {"name": "Alice", "score": 85, "action": "View"},
            {"name": "Bob", "score": 72, "action": "View"},
            {"name": "Carol", "score": 90, "action": "View"},
        ]
    )

    def on_button(row):
        if row < len(data):
            d = data[row]
            app.msg_box("Details", f"{d['name']}\nScore: {d['score']}")

    app.build(
        window=Window(
            "Table Buttons",
            450,
            300,
            child=VBox(
                Label(text=status),
                stretchy(
                    DataTable(
                        data,
                        TextColumn("Name", key="name"),
                        ProgressColumn("Score", key="score"),
                        ButtonColumn("Action", text_key="action", on_click=on_button),
                        on_row_clicked=lambda row: status.set(
                            f"Selected: {data[row]['name']}"
                        ),
                    )
                ),
            ),
        )
    )

    app.show()
    await app.wait()


libui.run(main())
Table with buttons

Column types reference

Column

Description

Key parameters

TextColumn

Text display/edit

key, editable, color_col

CheckboxColumn

Checkbox

key, editable

CheckboxTextColumn

Checkbox + text

checkbox_key, text_key, checkbox_editable, text_editable

ProgressColumn

Progress bar (0-100)

key

ButtonColumn

Clickable button

text_key, on_click, clickable

ImageColumn

Image display

key

ImageTextColumn

Image + text

image_key, text_key, editable

Table events

DataTable supports several event callbacks:

DataTable(
    data,
    *columns,
    on_row_clicked=lambda row: ...,          # single click
    on_row_double_clicked=lambda row: ...,   # double click
    on_header_clicked=lambda col: ...,       # header click
    on_selection_changed=lambda: ...,        # selection change
)

Dynamic updates

Since DataTable is backed by ListState, any mutation automatically updates the UI:

data.append({"name": "New item", "score": 50})  # row appears
data[0] = {"name": "Updated", "score": 99}       # row updates
data.pop()                                         # row disappears