Table

Powerful table and datagrids with built-in features.

Installation

Command to install the Table component.

Component Guide

Use the following to build the Table component.

from components.ui.table import table

Source Code & Dependencies

For manual installation, copy each of the source codes below in their respective locations.

components/core/core.py

components/ui/input.py

components/ui/table.py

Examples

Basic Table

Acme Studiohello@acmestudio.io
INV-2026-047Issued Jun 17, 2026, due Jul 17, 2026
Billed to Northgate Holdings Ltd. Net 30 payment terms apply.
DescriptionQtyUnit PriceAmount
Brand identity design1$3,200.00$3,200.00
UI component library1$5,800.00$5,800.00
Content strategy workshop3$450.00$1,350.00
Copywriting: landing pages5$280.00$1,400.00
QA & usability testing8$95.00$760.00
Project management12$75.00$900.00
Subtotal$13,410.00
Tax (8%)$1,072.80
Total Due$14,482.80

Table Invoice

Acme Inc.

Invoices

Recent billing activity across all client projects.

Outstanding$20,050.00


InvoiceStatusActions
INV-0041
Miriam Okafor
Pending$4,200.00
INV-0040
Theo Hartmann
Paid$1,850.00
INV-0039
Suki Nakamura
Overdue$6,500.00
INV-0038
Elias Ferreira
Paid$9,000.00
INV-0037
Priya Menon
Refunded$780.00
INV-0036
Dmitri Volkov
Paid$3,350.00
INV-0035
Amara Diallo
Pending$5,400.00
INV-0034
Noah Bergström
Paid$2,100.00
INV-0033
Lucia Romano
Overdue$3,950.00
INV-0032
Kwame Mensah
Paid$1,280.00

Figures shown in USD. Last updated Jun 17, 2026.

API Reference

table.root

The main container component. Renders the table shell — optional search toolbar, the scrollable <table>, and an optional pagination footer — and initializes the client-side controller that handles sorting, search, pagination, and row selection entirely in JS (no server round-trip).

If you plan to place a table.search() somewhere outside the table, give id an explicit, stable value so it can be targeted reliably.

table.root(
    table.header(...),
    table.body(...),
    id="invoices-table",
    searchable=True,
    paginate=True,
    page_size=10,
)
Prop Type Default
searchable bool False
paginate bool False
page_size int 5
id str Auto-generated
class_name str ""

table.search

A standalone search input that can be placed anywhere on the page — inside the table, in a page header, in a sidebar — and still filters a specific table. It's linked purely by for_table matching that table's id, using an event listener on the document rather than DOM position, so mount order doesn't matter. Multiple table.search() instances can target the same table.

table.search(
    for_table="invoices-table",
    placeholder="Search invoices…",
)
Prop Type Default
for_table str Required
placeholder str "Search…"
class_name str ""

table.header

Wraps the header row(s) in a sticky, styled <thead>.

table.header(
    table.row(
        table.head("Name"),
        table.head("Amount", sort_key="amount"),
    )
)
Prop Type Default
class_name str ""

table.body

Wraps the data rows in <tbody>. This is the element the client-side controller reads and reorders — sorting, search, and pagination all operate on the <tr> children found here.

table.body(
    table.row(
        table.cell("Brand identity design"),
        table.cell("$3,200.00", sort_value=3200.0),
    )
)
Prop Type Default
class_name str ""

table.footer

An optional <tfoot> for summary or total rows, rendered below the body and excluded from sorting/search/pagination.

table.footer(
    table.row(
        table.cell("Total", col_span=2),
        table.cell("$12,450.00"),
    )
)
Prop Type Default
class_name str ""

table.row

A single <tr>. Used inside table.header, table.body, or table.footer.

table.row(
    table.cell("Invoice"),
    table.cell("Client"),
)
Prop Type Default
class_name str ""

table.head

A <th> header cell. Passing sort_key turns it into a clickable sortable column, wiring up the click handler and the chevron sort-direction indicators automatically. Omit it for a plain, non-sortable header.

table.head("Client")                          # plain
table.head("Amount", sort_key="amount", class_name="text-right")  # sortable
Prop Type Default
sort_key str \| None None
class_name str ""

table.cell

A <td> data cell. By default, sorting compares each cell's rendered text. Pass sort_value whenever the displayed text isn't directly sortable — formatted currency, dates, or any derived/computed value — and it'll be compared instead.

table.cell(
    "$3,200.00",
    sort_value=3200.0,
    class_name="text-right",
)
Prop Type Default
sort_value Any None
class_name str ""

table.caption

An optional <caption>, rendered below the table for a lightweight description or footnote. Not involved in sorting/search/pagination.

table.caption(
    "Billed to ",
    rx.el.span("Acme Inc.", class_name="font-medium"),
)
Prop Type Default
class_name str ""

Row selection (opt-in convention)

There's no dedicated table.select_all() / table.select() component — selection is wired through plain checkbox.root() calls plus two data attributes the controller looks for. This keeps the checkbox styling entirely in your control.

# Header — select-all checkbox
table.head(
    checkbox.root(checkbox.indicator(), **{"data-dt-select-all": "true"}),
    class_name="w-10",
)

# Row — must have a unique `value` (e.g. a row id)
table.cell(
    checkbox.root(
        checkbox.indicator(),
        value=invoice["id"],
        **{"data-dt-select": "true"},
    )
)

Selection state lives in the browser only. To read it (e.g. for a "Delete selected" button), listen for the dt-selection-change custom event dispatched on the table's root element:

rx.el.div(
    ...,
    on_mount=rx.call_script(
        """
        document.getElementById('invoices-table').addEventListener(
            'dt-selection-change',
            (e) => console.log(e.detail.selected)
        )
        """
    ),
)

e.detail.selected is a list of the values currently checked, kept in sync across sorting, search, and pagination.