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 tableSource 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
| Description | Qty | Unit Price | Amount |
|---|---|---|---|
| Brand identity design | 1 | $3,200.00 | $3,200.00 |
| UI component library | 1 | $5,800.00 | $5,800.00 |
| Content strategy workshop | 3 | $450.00 | $1,350.00 |
| Copywriting: landing pages | 5 | $280.00 | $1,400.00 |
| QA & usability testing | 8 | $95.00 | $760.00 |
| Project management | 12 | $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.
| Invoice | Status | Actions | |||
|---|---|---|---|---|---|
| INV-0041 | Pending | $4,200.00 | |||
| INV-0040 | Paid | $1,850.00 | |||
| INV-0039 | Overdue | $6,500.00 | |||
| INV-0038 | Paid | $9,000.00 | |||
| INV-0037 | Refunded | $780.00 | |||
| INV-0036 | Paid | $3,350.00 | |||
| INV-0035 | Pending | $5,400.00 | |||
| INV-0034 | Paid | $2,100.00 | |||
| INV-0033 | Overdue | $3,950.00 | |||
| INV-0032 | 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,
)
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…",
)
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"),
)
)
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),
)
)
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"),
)
)
table.row
A single <tr>. Used inside table.header, table.body, or table.footer.
table.row(
table.cell("Invoice"),
table.cell("Client"),
)
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
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",
)
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"),
)
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.