data

<tc-table>

Sortable, filterable, paginated data table with keyed rows and custom cell renderers.

import "@ra9/tan-compose-kit/table"

Props

NameTypeDefaultDescription
rowsArray<Record<string, unknown>>[]The data rows. Each row's `id` field is used as the stable key by default.
columnsArray<{ key: string; label: string; sortable?: boolean; render?: (row) => string }>[]Column definitions. `render` returns raw HTML and is only available when columns are set as a JS property (functions don't survive JSON attributes).
pageSizenumber10How many rows to show per page.
filterablebooleantrueShow the search input above the table.
emptyTextstring"No results."Message shown when no rows match the filter.
rowKeystring"id"Property name used as the stable row key. Identical IDs across renders reuse DOM nodes.

Events

EventDetailWhen
tc-row-click{ row: object }Fires when the user clicks a row.
tc-sort-change{ key: string | null, direction: "asc" | "desc" | null }Fires when the user clicks a sortable header. Cycles asc → desc → unsorted.

CSS variables

VariableDefaultDescription
--tc-table-surfacevar(--tc-color-surface, #ffffff)Table background.
--tc-table-inkvar(--tc-color-ink, #14171f)Cell text color.
--tc-table-softvar(--tc-color-ink-soft, #5a6072)Header + pager text color.
--tc-table-rulevar(--tc-color-rule, #ece5d3)Border + row-divider color.
--tc-table-head-bgvar(--tc-color-surface-alt, #faf8f3)Header row background.
--tc-table-row-hoverrgba(161, 105, 57, 0.05)Row hover background.
--tc-table-accentvar(--tc-color-accent, #a16939)Sortable hover + focus color.
--tc-table-radiusvar(--tc-radius-lg, 10px)Outer border-radius.
--tc-table-fontvar(--tc-font-sans, …)Font family.

Examples

Basic usage

Pass rows and columns as JSON. Each column needs a key matching a row property, plus a human label.

<tc-table rows='[ {"id":1,"name":"Carlos","role":"Engineer"}, {"id":2,"name":"Naomi","role":"Designer"} ]' columns='[ {"key":"name","label":"Name"}, {"key":"role","label":"Role"} ]' ></tc-table>

Sorting

Click a column header to sort ascending, click again for descending, click a third time to clear. Disable per column with sortable: false.

<tc-table rows='[…]' columns='[ {"key":"name","label":"Name"}, {"key":"updated","label":"Updated","sortable":false} ]' ></tc-table>

Filtering + pagination

The search input is shown when filterable is true (the default). It matches against every column's stringified value. Use pageSize to set the page count.

<tc-table page-size="25" rows='[…]' columns='[…]'></tc-table>

Hide the search input for small datasets:

<tc-table filterable="false" rows='[…]' columns='[…]'></tc-table>

Custom cell renderers

Set columns as a JS property to use the render function. It returns raw HTML — escape your own values.

<tc-table id="orders"></tc-table> <script type="module"> const el = document.getElementById("orders"); el.rows = await fetchOrders(); el.columns = [ { key: "id", label: "Order" }, { key: "total", label: "Total", render: (r) => `$${r.total.toFixed(2)}` }, { key: "status", label: "Status", render: (r) => `<tc-badge variant="${r.status === "paid" ? "success" : "warning"}">${r.status}</tc-badge>`, }, ]; </script>

Listening for clicks

<tc-table id="people"></tc-table> <script type="module"> const el = document.getElementById("people"); el.addEventListener("tc-row-click", (e) => { console.log("opened", e.detail.row); }); </script>

Theming

<tc-table style="--tc-table-head-bg: #f0ead6; --tc-table-radius: 4px;" rows='[…]' columns='[…]' ></tc-table>

Accessibility

  • aria-sort on each sortable header announces the current state to screen readers.
  • The search input has a placeholder but no visible label — consider wrapping in your own labelled region if you need stricter compliance.
  • Row clicks emit tc-row-click; the row itself doesn't have a role="button", so wire up your own keyboard handler if the row needs to be activatable from the keyboard.
  • The pager buttons are real <button>s — Tab-reachable and Enter-activatable.