Table
The Table component provides you with data tables with sorting, pagination, and selection.
Live Examples
View Code
View Code
View Code
Import
import { Table } from "@jarllyng/nostromo";
// or
import { Table } from "@jarllyng/nostromo/table";Usage
import { Table, type TableColumn } from "@jarllyng/nostromo";
// A type alias rather than an interface. `data` is typed `Record<string,
// unknown>[]`, and an interface has no index signature, so TypeScript refuses
// it where an equivalent alias is accepted.
type User = {
id: string;
name: string;
email: string;
};
export default function Example() {
const data: User[] = [
{ id: "1", name: "John Doe", email: "john@example.com" },
{ id: "2", name: "Jane Smith", email: "jane@example.com" },
];
const columns: TableColumn<(typeof data)[number]>[] = [
{ key: "name", title: "Name", dataIndex: "name" },
{ key: "email", title: "Email", dataIndex: "email" },
];
return <Table data={data} columns={columns} />;
}Advanced Examples
With Sorting
View Code
With Pagination
View Code
Props
Table
| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
data | T[] | - | Yes | Array of data objects |
columns | TableColumn<T>[] | - | Yes | Column definitions |
variant | "default" | "striped" | "bordered" | "hover" | "elevated" | "interactive" | "default" | No | Visual variant of the table |
size | "sm" | "md" | "lg" | "md" | No | Size of table cells |
loading | boolean | false | No | Whether the table is loading |
emptyText | string | "No data" | No | Text to show when no data |
caption | string | undefined | No | Table caption (for accessibility) |
className | string | undefined | No | Additional CSS classes |
rowKey | keyof T | ((record: T) => string | number) | "id" | No | Unique key for each row |
onRowClick | (record: T, index: number) => void | undefined | No | Callback when row is clicked |
onSort | (column: TableColumn<T>, direction: 'asc' | 'desc') => void | undefined | No | Callback when column is sorted |
sortColumn | string | undefined | No | Currently sorted column (controlled) |
sortDirection | 'asc' | 'desc' | undefined | No | Sort direction (controlled) |
pagination | PaginationProps | undefined | No | Pagination configuration |
selection | SelectionProps | undefined | No | Row selection configuration |
TableColumn
| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
key | string | - | Yes | Unique column key |
title | string | - | Yes | Column header text |
dataIndex | keyof T | undefined | No | Property name in data object |
render | (value: unknown, record: T, index: number) => React.ReactNode | undefined | No | Custom render function |
sortable | boolean | false | No | Whether column is sortable |
width | string | number | undefined | No | Column width |
align | 'left' | 'center' | 'right' | 'left' | No | Text alignment |
className | string | undefined | No | Additional CSS classes |
PaginationProps
| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
current | number | - | Yes | Current page number |
pageSize | number | - | Yes | Number of items per page |
total | number | - | Yes | Total number of items |
onChange | (page: number, pageSize: number) => void | - | Yes | Callback when page changes |
showSizeChanger | boolean | false | No | Whether to show page size changer |
showQuickJumper | boolean | false | No | Whether to show quick jumper |
SelectionProps
| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
selectedRowKeys | (string | number)[] | - | Yes | Array of selected row keys |
onChange | (selectedRowKeys: (string | number)[], selectedRows: T[]) => void | - | Yes | Callback when selection changes |
getCheckboxProps | (record: T) => { disabled?: boolean } | undefined | No | Function to get checkbox props for each row |
Selection spans pages, the checkbox does not
selectedRowKeys is the whole selection. It can name records that are not on the
page being rendered, and the Table never discards them.
That shapes two things worth knowing.
Select-all acts on this page. It adds every selectable row you can currently see to the selection, or removes them, and leaves every other key untouched. Its checked state is read the same way: all of the selectable rows on this page, some of them, or none. Keys belonging to other pages neither check it nor leave it indeterminate.
A disabled row is skipped, not selected for you. getCheckboxProps returning
{ disabled: true } takes the row out of select-all as well as out of its own
checkbox. Once every row that can be selected is selected, select-all reads as
checked rather than staying permanently indeterminate because of the rows that
cannot be.
The keys are authoritative, the records are what is on screen
onChange gives you both:
onChange={(keys, rows) => { ... }}keys is the complete selection. rows is only the subset present in data,
because the component has never been given the rest. If you paginate server-side,
keep your own key-to-record map and treat the keys as the source of truth.
Zero is a key
rowKey falls back to the row’s index only when the value is null or
undefined. A record with id: 0, or an empty-string key, keeps its own key.