Table
Data table built on TanStack Table v8. Aurora wraps TanStack with sensible aurora-flavored defaults: sorting indicators, pagination, column visibility, row selection, and persistence via the DS_TABLE_SETTINGS adapter.
1–11 general.of 11
The column schema uses TanStack's createColumnHelper<T>() — pass your row type for cell-typed safety.
When to reach for a table
- Structured datasets with multiple comparable attributes per row — the user wants to scan, sort, or compare across columns
- ESG data — buildings, meters, certifications, surveys, audits
- Admin views and audit logs — long lists where filtering and bulk action matter
- Sortable, filterable, or bulk-actionable content — selection + the floating action bar live here
When not to use a table
- Small datasets (handful of items, few attributes) — cards or a plain list read better
- Single-column content — that's a list, not a table
- Deeply nested hierarchies — use a tree view
- A form layout — fields belong in
Input/Combobox/ etc., laid out as a form. Tables are for displaying rows of data.
Basic example (everything enabled)
vue
<script setup lang="ts">
import { ref } from 'vue'
import { Table } from '@scaler-tech/aurora/table'
import { createColumnHelper } from '@tanstack/vue-table'
type Person = { id: number; firstName: string; lastName: string; age: number; status: string }
const columnHelper = createColumnHelper<Person>()
const columns = [
columnHelper.accessor('id', { header: 'ID', cell: info => info.getValue() }),
columnHelper.accessor('firstName', { header: 'First Name', cell: info => info.getValue() }),
columnHelper.accessor('lastName', { header: 'Last Name', cell: info => info.getValue() }),
columnHelper.accessor('age', { header: 'Age', cell: info => info.getValue() }),
columnHelper.accessor('status', { header: 'Status', cell: info => info.getValue() }),
]
const data = ref<Person[]>([
{ id: 1, firstName: 'John', lastName: 'Doe', age: 25, status: 'Active' },
/* … */
])
</script>
<Table
:data="data"
:columns="columns"
row-id-key="id"
:enable-sorting
:enable-column-resizing
:enable-pagination
:enable-column-visibility
:enable-row-selection
/>Minimal — no extra features
Pagination, sorting, and the rest are off by default-ish; pass exactly what you want.
html
<Table :data="rows" :columns="columns" row-id-key="id" :enable-pagination="false" />Loading
1–3 general.of 3
html
<Table :data="data" :columns="columns" row-id-key="id" :loading="true" />Props (subset — most-used)
| Prop | Type | Default | Description |
|---|---|---|---|
data | T[] | required | Row data array. |
columns | ColumnDef<T>[] | required | Column definitions from createColumnHelper<T>(). |
rowIdKey | keyof T (string/number-valued) | required | Field that uniquely identifies a row — feeds TanStack's getRowId, so selection, expansion, and reorder state survive data refreshes. Falls back to the array index for rows missing the value. |
enableSorting | boolean | true | Show sort indicators in headers; clicking toggles sort. |
enableColumnResizing | boolean | true | Drag the column edge to resize. |
enablePagination | boolean | true | Render pagination footer. |
enableColumnVisibility | boolean | false | Show the column-toggle menu in the header. |
enableRowSelection | boolean | false | Render checkbox column. |
enableRowReordering | boolean | false | Drag rows to reorder. |
enableColumnReordering | boolean | false | Drag columns to reorder. |
enableColumnFilters | boolean | false | Per-column filter UI. |
enableGlobalFilter | boolean | false | Global search input. |
loading | boolean | false | Show loading skeletons. |
pagination | { pageIndex?: number; pageSize?: number; pageSizeOptions?: number[]; serverSide?: boolean } | { pageSize: 25, pageSizeOptions: [10, 25, 50, 100] } | Pagination configuration. |
columnPinning | { left?: string[]; right?: string[] } | { left: [], right: [] } | Pin columns to the start/end. |
pinnedTopRowIds | string[] | [] | Ids of rows to render above the others (TanStack row pinning). See "Pinned rows". |
pinnedRowClass | string | — | Classes applied to every cell of a pinned row, replacing its white background. |
defaultColumnSizing | { minSize?: number; maxSize?: number; size?: number } | { size: 0, minSize: 0 } | Default sizing applied to all columns. |
showInternalBorders | boolean | true | Render borders between cells. |
hideInternalVerticalBorders | boolean | false | Hide vertical cell borders only (rows-only dividers). |
hideInternalHorizontalBorders | boolean | false | Hide horizontal cell borders only (columns-only dividers). |
roundedCorners | boolean | — | Round the outer corners. |
stickyScrollbar | boolean | — | Pin the horizontal scrollbar to the viewport bottom. |
stickyHeader | boolean | false | Pin the header to the top of the scroll region without fill mode. Only takes visual effect where the table's own scroll box is the vertical scroller. |
groupBy | string | ((row: T) => string) | — | Group rows under full-width collapsible header rows. See "Grouped rows". |
The data and columns props are TanStack-shaped — refer to TanStack Table docs for advanced column features (filter functions, sort functions, sub-rows, expanding, custom cell renderers).
Custom cell rendering
Use a slot named after the column id to override that column's cell rendering — no need to provide a cell function in the column definition.
html
<Table :data="data" :columns="columns" row-id-key="id">
<template #status="{ value }">
<Tag :variant="value === 'Active' ? 'success' : 'neutral'">{{ value }}</Tag>
</template>
</Table>The slot props give you { cell, row, original, value, index } — value is typed as the column's cell value when you use createColumnHelper<T>().
Pinned rows
pinnedTopRowIds lifts named rows above the rest — a totals or summary band that stays put while the reader pages through the records. The pinned rows must be in data (TanStack can only pin a row it knows about) and their id must be a string, so it can never collide with a numeric record id.
html
<Table
:data="[summaryRow, ...assets]"
:columns="columns"
row-id-key="id"
:pinned-top-row-ids="['summary']"
pinned-row-class="bg-secondary"
/>- Pinned rows show above every page, in the order given, and are excluded from the pagination footer's count — they read as a summary, not as one more record.
- Cell slots receive the pinned row like any other, so branch on your own flag (
original.summary) to render it differently. pinnedRowClasstints the band. It replaces the cell's own background rather than layering over it, so pass one background utility.
Grouped rows
Pass groupBy to section the table: each group gets a full-width, collapsible header row above its rows (built on TanStack's grouping + expanded row models). Pass a column id to group by that column's value, or an accessor function when the grouping isn't a column of its own — the returned string doubles as the header label.
html
<Table :data="assets" :columns="columns" row-id-key="id" :group-by="row => row.statusBand" />- Groups keep the data's order of first appearance — sort your rows before grouping to control group order.
- Groups start expanded; clicking a header collapses it.
- The default header renders the label and the group's row count. Override it with the
group-headerslot ({ row, label, count, isExpanded, colspan }); aggregates are available via TanStack — putaggregationFnon a column def and readrow.getValue(columnId)on the group row.
html
<Table :data="assets" :columns="columns" row-id-key="id" group-by="assetType">
<template #group-header="{ label, count, row }">
<span class="paragraph-xs-medium text-primary">{{ label }}</span>
<span class="paragraph-xs text-secondary">{{ count }} assets · avg {{ row.getValue('intensity') }}</span>
</template>
</Table>Grouping is client-side. With server-side pagination it only groups the loaded page, and header rows count toward the page size — it pairs best with enablePagination: false.
Persistence (DS_TABLE_SETTINGS)
Column order, row order, column visibility, and saved views all persist through the DS_TABLE_SETTINGS adapter. Aurora ships a no-op default — Table renders without persistence out of the box. Provide a real adapter at the app root via provideTableSettings(app) to enable saving.