Table
Shows large data sets that people sort, select and resize.
@paneui/theme@paneui/icons@paneui/checkbox@paneui/loader@paneui/scroll-area@tanstack/react-virtualmotion@base-ui/react
Installation
pnpm dlx shadcn@latest add @paneui/tableUsage
Examples
Scrollbars
The table scrolls in a Scroll Area. The vertical track starts below the sticky header and both tracks stay inside the rounded frame.
Keyboard
Sortable headers are buttons (asc → desc → off). With reorderable, Alt + ← / → moves the focused column; with resizable, focus a column edge and use ← / → (Shift for bigger steps), or double-click it to reset. Shift-click a checkbox to select a range.
Related
Table Editable and Table Async build on this core.
Table
Virtualized data table. Renders a div tree with table / grid roles; no other DOM props pass through.
- data*
- readonly T[]
- Rows.
- columns*
- readonly TableColumn<T>[]
- Column definitions.
- getRowId
- (row: T, index: number) => string
- Stable id per row; keeps selection and animation correct across sorts. Defaults to the index.
- aria-label
- string
- Accessible name.
- role= "table"
- "table" | "grid"
gridfor interactive, spreadsheet-style cells.- selectable= false
- boolean
- Add a checkbox lane; shift-click selects ranges.
- selectedRowIds
- readonly string[]
- Controlled selection.
- defaultSelectedRowIds
- readonly string[]
- Initial selection.
- onSelectionChange
- (ids: string[]) => void
- Called when the selection changes.
- sort
- { key: string; direction: "asc" | "desc" } | null
- Controlled sort.
- defaultSort= null
- { key: string; direction: "asc" | "desc" } | null
- Initial sort.
- onSortChange
- (sort: SortState) => void
- Called when a header cycles the sort.
- resizable= false
- boolean
- Drag column edges to resize.
- minColumnWidth= 72
- number
- Resize floor in px.
- onColumnResize
- (key: string, width: number) => void
- Called after a resize.
- reorderable= false
- boolean
- Drag headers to reorder.
- columnOrder
- readonly string[]
- Controlled column order (keys).
- defaultColumnOrder
- readonly string[]
- Initial column order.
- onColumnOrderChange
- (keys: string[]) => void
- Called after a reorder.
- rowHeight= 44
- number
- Row height in px.
- headerHeight= 40
- number
- Header height in px.
- height= 440
- number
- Viewport height in px.
- overscan= 8
- number
- Rows rendered beyond the viewport; rows further out are not rendered.
- loading= false
- boolean
- Skeleton rows when there is no data yet, a loading footer otherwise; pauses
onEndReached. - skeletonRows
- number
- Number of skeleton rows while loading with no data.
- onEndReached
- () => void
- Called when the list nears its end.
- endReachedThreshold= 8
- number
- How many rows from the end
onEndReachedfires. - footer
- ReactNode
- A band under the last row (load more, add row, end of list).
- emptyState= "No rows"
- ReactNode
- Shown when there are no rows and
loadingis off. - renderCell
- (ctx: TableCellContext<T> & { content: ReactNode }) => ReactNode
- Wrap or replace any cell;
contentis what the table would have rendered. - renderHeader
- (ctx: TableHeaderContext<T>) => ReactNode
- Replace a header label; spread
labelPropsonto your element. - headerAccessory
- (ctx: TableHeaderContext<T>) => ReactNode
- Extra content inside a header cell, e.g. a column menu.
- gutter
- { width: number; header?: ReactNode; cell: (ctx: TableRowContext<T>) => ReactNode }
- Leading lane before the checkbox, e.g. row handles.
- onRowClick
- (ctx: TableRowContext<T>, event: MouseEvent) => void
- Called when a row is clicked.
- rowClassName
- (ctx: TableRowContext<T>) => string | undefined
- Class per row from its context, e.g.
text-muted-foregroundfor archived rows. Tokens only. - animateLayout= true
- boolean
- Slide rows and columns into new positions on insert, delete, sort and reorder. Off under reduced motion.
- className
- string
- Class for the outer surface.
- ref
- Ref<TableHandle>
scrollToIndex(index, { align, behavior }),scrollToTop(behavior)andelement().
| Prop | Type | Default | Description |
|---|---|---|---|
| data* | readonly T[] | — | Rows. |
| columns* | readonly TableColumn<T>[] | — | Column definitions. |
| getRowId | (row: T, index: number) => string | — | Stable id per row; keeps selection and animation correct across sorts. Defaults to the index. |
| aria-label | string | — | Accessible name. |
| role | "table" | "grid" | "table" | grid for interactive, spreadsheet-style cells. |
| selectable | boolean | false | Add a checkbox lane; shift-click selects ranges. |
| selectedRowIds | readonly string[] | — | Controlled selection. |
| defaultSelectedRowIds | readonly string[] | — | Initial selection. |
| onSelectionChange | (ids: string[]) => void | — | Called when the selection changes. |
| sort | { key: string; direction: "asc" | "desc" } | null | — | Controlled sort. |
| defaultSort | { key: string; direction: "asc" | "desc" } | null | null | Initial sort. |
| onSortChange | (sort: SortState) => void | — | Called when a header cycles the sort. |
| resizable | boolean | false | Drag column edges to resize. |
| minColumnWidth | number | 72 | Resize floor in px. |
| onColumnResize | (key: string, width: number) => void | — | Called after a resize. |
| reorderable | boolean | false | Drag headers to reorder. |
| columnOrder | readonly string[] | — | Controlled column order (keys). |
| defaultColumnOrder | readonly string[] | — | Initial column order. |
| onColumnOrderChange | (keys: string[]) => void | — | Called after a reorder. |
| rowHeight | number | 44 | Row height in px. |
| headerHeight | number | 40 | Header height in px. |
| height | number | 440 | Viewport height in px. |
| overscan | number | 8 | Rows rendered beyond the viewport; rows further out are not rendered. |
| loading | boolean | false | Skeleton rows when there is no data yet, a loading footer otherwise; pauses onEndReached. |
| skeletonRows | number | — | Number of skeleton rows while loading with no data. |
| onEndReached | () => void | — | Called when the list nears its end. |
| endReachedThreshold | number | 8 | How many rows from the end onEndReached fires. |
| footer | ReactNode | — | A band under the last row (load more, add row, end of list). |
| emptyState | ReactNode | "No rows" | Shown when there are no rows and loading is off. |
| renderCell | (ctx: TableCellContext<T> & { content: ReactNode }) => ReactNode | — | Wrap or replace any cell; content is what the table would have rendered. |
| renderHeader | (ctx: TableHeaderContext<T>) => ReactNode | — | Replace a header label; spread labelProps onto your element. |
| headerAccessory | (ctx: TableHeaderContext<T>) => ReactNode | — | Extra content inside a header cell, e.g. a column menu. |
| gutter | { width: number; header?: ReactNode; cell: (ctx: TableRowContext<T>) => ReactNode } | — | Leading lane before the checkbox, e.g. row handles. |
| onRowClick | (ctx: TableRowContext<T>, event: MouseEvent) => void | — | Called when a row is clicked. |
| rowClassName | (ctx: TableRowContext<T>) => string | undefined | — | Class per row from its context, e.g. text-muted-foreground for archived rows. Tokens only. |
| animateLayout | boolean | true | Slide rows and columns into new positions on insert, delete, sort and reorder. Off under reduced motion. |
| className | string | — | Class for the outer surface. |
| ref | Ref<TableHandle> | — | scrollToIndex(index, { align, behavior }), scrollToTop(behavior) and element(). |
- data-selected
- On selected rows.
- data-row-id
- The row id, on each row.
- data-col-key
- The column key, on header and body cells.
- data-dragging
- On a header while it is being dragged.
- data-resizing
- On a resize handle while it is being dragged.
| Data attribute | Description |
|---|---|
| data-selected | On selected rows. |
| data-row-id | The row id, on each row. |
| data-col-key | The column key, on header and body cells. |
| data-dragging | On a header while it is being dragged. |
| data-resizing | On a resize handle while it is being dragged. |
TableColumn
Type of each entry in columns.
- key*
- string
- Stable key; also the property read from the row when there is no
accessor. - header*
- ReactNode
- Header label.
- width= 160
- number
- Width in px.
- minWidth
- number
- Floor for resizing. Falls back to
minColumnWidth. - grow
- boolean
- Soak up leftover width (until resized).
- align= "left"
- "left" | "center" | "right"
- Cell alignment.
- sortable
- boolean
- Make the header sort on click.
- resizable
- boolean
- Set
falseto opt out of resizing. - accessor
- (row: T) => unknown
- Raw value for the cell, sorting and default rendering.
- sortValue
- (row: T) => string | number | null | undefined
- Value used for sorting when it differs from the accessor.
- cell
- (row: T, ctx: TableCellContext<T>) => ReactNode
- Custom cell content.
- className
- string
- Class for body cells.
- headerClassName
- string
- Class for the header cell.
| Prop | Type | Default | Description |
|---|---|---|---|
| key* | string | — | Stable key; also the property read from the row when there is no accessor. |
| header* | ReactNode | — | Header label. |
| width | number | 160 | Width in px. |
| minWidth | number | — | Floor for resizing. Falls back to minColumnWidth. |
| grow | boolean | — | Soak up leftover width (until resized). |
| align | "left" | "center" | "right" | "left" | Cell alignment. |
| sortable | boolean | — | Make the header sort on click. |
| resizable | boolean | — | Set false to opt out of resizing. |
| accessor | (row: T) => unknown | — | Raw value for the cell, sorting and default rendering. |
| sortValue | (row: T) => string | number | null | undefined | — | Value used for sorting when it differs from the accessor. |
| cell | (row: T, ctx: TableCellContext<T>) => ReactNode | — | Custom cell content. |
| className | string | — | Class for body cells. |
| headerClassName | string | — | Class for the header cell. |
TableSkeletonRow
Placeholder row for custom loading states.
- index*
- number
- Row index; varies the bar widths.
- cells*
- readonly number[]
- Cell widths in px, in display order.
- height*
- number
- Row height in px.
- className
- string
- Class for the row.
| Prop | Type | Default | Description |
|---|---|---|---|
| index* | number | — | Row index; varies the bar widths. |
| cells* | readonly number[] | — | Cell widths in px, in display order. |
| height* | number | — | Row height in px. |
| className | string | — | Class for the row. |
TableLoadingFooter
Default “Loading more…” band with a spinner, for footer.
- label= "Loading more…"
- ReactNode
- Text beside the spinner.
| Prop | Type | Default | Description |
|---|---|---|---|
| label | ReactNode | "Loading more…" | Text beside the spinner. |