Table

Virtualized data table that stays smooth at 10k+ rows, with sortable headers, row selection, optional bulk-action headers, column resize and reorder, and a sticky header. Minimal, reduced-motion-safe motion.

Data Table

index.tsx

10k virtualized rows with sortable headers, row selection, column resize and reorder.

10,000 rows
Email
Status

Install

$ bunx --bun shadcn@latest add @beui/table

Bulk Actions

index.tsx

Select rows to replace column controls with a selected count and custom selectionActions. Activate or delete selected members, clear selection to restore the header, and keep column widths and sorting intact.

Select rows to turn the header into bulk actions.

Email

Install

$ bunx --bun shadcn@latest add @beui/table-selection

Editable Table

index.tsx

Edit cells inline and insert or delete rows and columns via border handles; the table re-renders from the updated data and column defs.

Click a cell to edit. Use the column and row handles to insert or delete.

Install

$ bunx --bun shadcn@latest add @beui/table-editable

Async Table

index.tsx

Loads pages on demand — skeleton rows on first load, then infinite scroll via onEndReached as the virtualized list nears the bottom.

0 loadedLoading…
Name
Email
Role
Status
MRR

Install

$ bunx --bun shadcn@latest add @beui/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)
—
selectionActions?
((selection: { selectedRows: T[]; selectedRowIds: string[]; clearSelection: () => void; }) => ReactNode)

Replaces column controls while loaded rows are selected; requires `selectable`. Rows/ids follow the current sort and exclude ids absent from `data`. `clearSelection` clears every selected id, including unloaded rows.

—
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
className?
string
—

Updated