Table

Two data tables: a virtualized Table that renders only the visible rows of large datasets, with sorting, row selection, resizable and movable columns and editable cells, and a paginated DataTable with filters and expandable rows. Both can turn rows into cards on narrow screens with mobileLayout="stack".

Data Table

index.tsx

Virtualized rows under a sticky header, click-to-sort columns, checkbox selection with select-all, and drag handles to resize or move columns; a fixed row height keeps 10,000 rows scrolling smoothly.

Table Data Table preview width

Install

$ bunx --bun shadcn add @karaui/table
@karaui not found? Add it to components.json
{
  "registries": {
    "@karaui": "https://karaui.kamisama-v0.my.id/r/{name}.json"
  }
}

@karaui is in shadcn's public registry directory, so the latest CLI needs no setup. On an older CLI, merge this into the existing file once and every @karaui/… install resolves.

API Reference

data
T[]
—
columns
TableColumn<T>[]
—
getRowId?
((row: T, index: number) => string)

Stable id per row, required for correct selection across sorts. Defaults to row index.

—
selectable?
boolean

Render a leading checkbox column with select-all in the header.

false
selectedRowIds?
string[]
—
defaultSelectedRowIds?
string[]
—
onSelectionChange?
((ids: string[]) => void)
—
sort?
SortState | null
—
defaultSort?
SortState | null
null
onSortChange?
((sort: SortState | null) => void)
—
resizable?
boolean

Allow dragging the right edge of a header to resize that column.

false
minColumnWidth?
number

Minimum column width in px when resizing.

64
onColumnResize?
((key: string, width: number) => void)
—
reorderable?
boolean

Allow dragging a header grip to reorder columns.

false
onColumnOrderChange?
((keys: string[]) => void)
—
onCellEdit?
((rowId: string, columnKey: string, value: string) => void)

Called when an `editable` cell changes.

—
onColumnRename?
((columnKey: string, value: string) => void)

When set, non-sortable headers become editable inputs for the column name.

—
onInsertRow?
((index: number, position: InsertPosition) => void)

Enables the row menu (Insert before / after). Receives the target index.

—
onDeleteRow?
((rowId: string, index: number) => void)

Enables Delete in the row menu.

—
onInsertColumn?
((index: number, position: InsertPosition) => void)

Enables the column menu (Insert before / after). Receives the target column index.

—
onDeleteColumn?
((columnKey: string, index: number) => void)

Enables Delete in the column menu.

—
rowHeight?
number

Fixed row height in px — required for virtualization.

48
height?
number

Scroll viewport height in px.

440
overscan?
number

Rows rendered above/below the viewport.

10
onEndReached?
(() => void)

Fires when the viewport scrolls near the bottom — load the next page.

—
loading?
boolean

Currently fetching — shows skeleton rows and pauses `onEndReached`.

false
skeletonRows?
number

How many skeleton rows to show while loading more (default 3).

3
emptyState?
ReactNode
No data
mobileLayout?
"scroll" | "stack"

`"scroll"` (default) keeps today's sideways-scrolling table. `"stack"` swaps to a virtualized card list (title from `primary` columns, the rest as header: value pairs, editable cells stay inputs) whenever the table's own width is below `stackBelow`. The server always renders the table.

scroll
stackBelow?
number

Width in px under which `mobileLayout="stack"` shows cards (default 560).

560
className?
string
—

Editable Table

index.tsx

Mark columns editable to type straight into cells, and use the row and column menus to add or remove rows and columns; your data and column definitions stay the single source of truth.

Table Editable Table preview width

Install

$ bunx --bun shadcn add @karaui/table-editable

API Reference

data
T[]
—
columns
TableColumn<T>[]
—
getRowId?
((row: T, index: number) => string)

Stable id per row, required for correct selection across sorts. Defaults to row index.

—
selectable?
boolean

Render a leading checkbox column with select-all in the header.

false
selectedRowIds?
string[]
—
defaultSelectedRowIds?
string[]
—
onSelectionChange?
((ids: string[]) => void)
—
sort?
SortState | null
—
defaultSort?
SortState | null
null
onSortChange?
((sort: SortState | null) => void)
—
resizable?
boolean

Allow dragging the right edge of a header to resize that column.

false
minColumnWidth?
number

Minimum column width in px when resizing.

64
onColumnResize?
((key: string, width: number) => void)
—
reorderable?
boolean

Allow dragging a header grip to reorder columns.

false
onColumnOrderChange?
((keys: string[]) => void)
—
onCellEdit?
((rowId: string, columnKey: string, value: string) => void)

Called when an `editable` cell changes.

—
onColumnRename?
((columnKey: string, value: string) => void)

When set, non-sortable headers become editable inputs for the column name.

—
onInsertRow?
((index: number, position: InsertPosition) => void)

Enables the row menu (Insert before / after). Receives the target index.

—
onDeleteRow?
((rowId: string, index: number) => void)

Enables Delete in the row menu.

—
onInsertColumn?
((index: number, position: InsertPosition) => void)

Enables the column menu (Insert before / after). Receives the target column index.

—
onDeleteColumn?
((columnKey: string, index: number) => void)

Enables Delete in the column menu.

—
rowHeight?
number

Fixed row height in px — required for virtualization.

48
height?
number

Scroll viewport height in px.

440
overscan?
number

Rows rendered above/below the viewport.

10
onEndReached?
(() => void)

Fires when the viewport scrolls near the bottom — load the next page.

—
loading?
boolean

Currently fetching — shows skeleton rows and pauses `onEndReached`.

false
skeletonRows?
number

How many skeleton rows to show while loading more (default 3).

3
emptyState?
ReactNode
No data
mobileLayout?
"scroll" | "stack"

`"scroll"` (default) keeps today's sideways-scrolling table. `"stack"` swaps to a virtualized card list (title from `primary` columns, the rest as header: value pairs, editable cells stay inputs) whenever the table's own width is below `stackBelow`. The server always renders the table.

scroll
stackBelow?
number

Width in px under which `mobileLayout="stack"` shows cards (default 560).

560
className?
string
—

Async Table

index.tsx

Fetches more as you near the end: skeleton rows fill in while loading, onEndReached asks for the next page, and the loading flag holds back further requests until it arrives.

Table Async Table preview width

Install

$ bunx --bun shadcn add @karaui/table-async

API Reference

data
T[]
—
columns
TableColumn<T>[]
—
getRowId?
((row: T, index: number) => string)

Stable id per row, required for correct selection across sorts. Defaults to row index.

—
selectable?
boolean

Render a leading checkbox column with select-all in the header.

false
selectedRowIds?
string[]
—
defaultSelectedRowIds?
string[]
—
onSelectionChange?
((ids: string[]) => void)
—
sort?
SortState | null
—
defaultSort?
SortState | null
null
onSortChange?
((sort: SortState | null) => void)
—
resizable?
boolean

Allow dragging the right edge of a header to resize that column.

false
minColumnWidth?
number

Minimum column width in px when resizing.

64
onColumnResize?
((key: string, width: number) => void)
—
reorderable?
boolean

Allow dragging a header grip to reorder columns.

false
onColumnOrderChange?
((keys: string[]) => void)
—
onCellEdit?
((rowId: string, columnKey: string, value: string) => void)

Called when an `editable` cell changes.

—
onColumnRename?
((columnKey: string, value: string) => void)

When set, non-sortable headers become editable inputs for the column name.

—
onInsertRow?
((index: number, position: InsertPosition) => void)

Enables the row menu (Insert before / after). Receives the target index.

—
onDeleteRow?
((rowId: string, index: number) => void)

Enables Delete in the row menu.

—
onInsertColumn?
((index: number, position: InsertPosition) => void)

Enables the column menu (Insert before / after). Receives the target column index.

—
onDeleteColumn?
((columnKey: string, index: number) => void)

Enables Delete in the column menu.

—
rowHeight?
number

Fixed row height in px — required for virtualization.

48
height?
number

Scroll viewport height in px.

440
overscan?
number

Rows rendered above/below the viewport.

10
onEndReached?
(() => void)

Fires when the viewport scrolls near the bottom — load the next page.

—
loading?
boolean

Currently fetching — shows skeleton rows and pauses `onEndReached`.

false
skeletonRows?
number

How many skeleton rows to show while loading more (default 3).

3
emptyState?
ReactNode
No data
mobileLayout?
"scroll" | "stack"

`"scroll"` (default) keeps today's sideways-scrolling table. `"stack"` swaps to a virtualized card list (title from `primary` columns, the rest as header: value pairs, editable cells stay inputs) whenever the table's own width is below `stackBelow`. The server always renders the table.

scroll
stackBelow?
number

Width in px under which `mobileLayout="stack"` shows cards (default 560).

560
className?
string
—

Advanced Data Table

data-table.tsx

The paginated DataTable: Shift+click to sort by several columns, per-column text or select filters plus a global search, a column visibility picker, column reordering, rows that expand into detail panels and range selection with a bulk-action bar.

Table Advanced Data Table preview width

Install

$ bunx --bun shadcn add @karaui/table-advanced

API Reference

data
T[]
—
columns
DataTableColumn<T>[]
—
getRowId?
((row: T, index: number) => string)

Stable id per row — keeps selection, expansion and the reorder animation attached to the right row.

—
getRowLabel?
((row: T) => string)

Accessible name of a row, used by its checkbox and expand button. Defaults to the first column's text.

—
sort?
SortState[]

Sort stack, primary first. Click a header to sort by it alone; Shift+click adds a secondary sort.

—
defaultSort?
SortState[]
[]
onSortChange?
((sort: SortState[]) => void)
—
filters?
DataTableFilters

Per-column filters keyed by column key.

—
defaultFilters?
DataTableFilters
{}
onFiltersChange?
((filters: DataTableFilters) => void)
—
search?
string

Global search across every `searchable` column.

—
defaultSearch?
string
onSearchChange?
((search: string) => void)
—
showSearch?
boolean

Render the search field in the toolbar (default true).

true
searchPlaceholder?
string
Search…
hiddenColumns?
string[]

Keys of hidden columns.

—
defaultHiddenColumns?
string[]
[]
onHiddenColumnsChange?
((keys: string[]) => void)
—
columnOrder?
string[]

Column key order.

—
defaultColumnOrder?
string[]
—
onColumnOrderChange?
((keys: string[]) => void)
—
reorderable?
boolean

Drag headers by their grip, or use Move left/right in the column menu (default true).

true
renderExpanded?
((row: T) => ReactNode)

Renders a chevron column; the returned node fills a height-morphing detail panel under the row.

—
expandedRowIds?
string[]
—
defaultExpandedRowIds?
string[]
[]
onExpandedChange?
((ids: string[]) => void)
—
selectable?
boolean

Leading checkbox column; Shift+click selects a range.

false
selectedRowIds?
string[]
—
defaultSelectedRowIds?
string[]
[]
onSelectionChange?
((ids: string[]) => void)
—
selectionActions?
((ids: string[]) => ReactNode)

Extra controls in the selected-count bar, e.g. bulk actions.

—
pageSize?
number

Rows per page; `0` shows every row without pagination (default 10).

10
page?
number

1-based page. Resets to 1 when search, filters or sort change.

—
defaultPage?
number
1
onPageChange?
((page: number) => void)
—
toolbar?
ReactNode

Extra toolbar content, rendered right of the search field.

—
emptyState?
ReactNode
No results
minColumnWidth?
number

Narrowest a column without an explicit px width may get before the table scrolls (default 120).

120
mobileLayout?
"scroll" | "stack"

`"scroll"` (default) keeps today's sideways-scrolling table. `"stack"` renders each row as a card (title from `primary` columns, the rest as header: value pairs, with selection, expand and a sort select plus filter chips above the list) whenever the table's own width is below `stackBelow`. The server always renders the table.

scroll
stackBelow?
number

Width in px under which `mobileLayout="stack"` shows cards (default 560).

560
className?
string
—

Updated