Table
Tables are used to display tabular data using rows and columns. They allow users to quickly scan, sort, compare, and take action on large amounts of data.
Installation
npx nextui-cli@latest add table
The above command is for individual installation only. You may skip this step if @nextui-org/react
is already installed globally.
Import
NextUI exports 6 table-related components:
- Table: The main component to display a table.
- TableHeader: The header of the table.
- TableBody: The body of the table.
- TableColumn: The column of the table.
- TableRow: The row of the table.
- TableCell: The cell of the table.
Usage
Dynamic
To render a table dynamically, you can use the columns
prop to pass the columns and items
prop to pass the data.
Why not array map?
Using the items
prop and providing a render function allows react-aria
to automatically cache the results of rendering each item and avoid re-rendering all items in the
collection when only one of them changes. This has big performance benefits for large
collections.
You could also use Array.map
to render the items, but it will not be as performant as using the items
and columns
prop.
Example:
import {Table, TableHeader, TableColumn, TableBody, TableRow, TableCell, getKeyValue} from "@nextui-org/react";const rows = [...];const columns = [...];export default function App() { return ( <Table aria-label="Example table with dynamic content"> <TableHeader> {columns.map((column) => <TableColumn key={column.key}>{column.label}</TableColumn> )} </TableHeader> <TableBody> {rows.map((row) => <TableRow key={row.key}> {(columnKey) => <TableCell>{getKeyValue(row, columnKey)}</TableCell>} </TableRow> )} </TableBody> </Table> );}
return (<Table aria-label="Example table with dynamic content"><TableHeader>{columns.map((column) =><TableColumn key={column.key}>{column.label}</TableColumn>)}</TableHeader><TableBody>{rows.map((row) =><TableRow key={row.key}>{(columnKey) => <TableCell>{getKeyValue(row, columnKey)}</TableCell>}</TableRow>)}</TableBody></Table>);}
Note: To learn more about React Aria collections and how to use them, please check React Aria Collections.
Empty State
You can use the emptyContent
prop to render a custom component when the table is empty.
Without Header
In case you don't want to render the header, you can use the hideHeader
prop.
Without Wrapper
By default the table is wrapped in a div
element with a small shadow effect and a border radius.
You can use the removeWrapper
prop to remove the wrapper and only render the table.
Custom Cells
You can render any component inside the table cell. In the example below, we are rendering different components according to the key
of the column.
Striped Rows
You can use the isStriped
prop to render striped rows.
Single Row Selection
It is possible to make the table rows selectable. To do so, you can use the selectionMode
prop. Use defaultSelectedKeys
to
provide a default set of selected rows.
Note: The value of the selected keys must match the key prop of the row.
Multiple Row Selection
You can also select multiple rows by using the selectionMode="multiple"
prop. Use defaultSelectedKeys
to
provide a default set of selected rows.
Note: When using multiple selection, selectable checkboxes will be rendered in the first column of the table.
Disallow Empty Selection
Table also supports a disallowEmptySelection
prop which forces the user to have at least one
row in the Table selected at all times. In this mode, if a single row is selected and the
user presses it, it will not be deselected.
Controlled Selection
To programmatically control row selection, use the selectedKeys
prop paired with the onSelectionChange
callback.
The key prop from the selected rows will be passed into the callback when the row is pressed, allowing you to update state accordingly.
Note: The
selectedKeys
property must be aSet
object.
Disabled Rows
You can disable rows by using the disabledKeys
prop. This will prevent rows from being
selectable as shown in the example below.
Selection Behavior
By default, Table uses the toggle
selection behavior, which behaves like a checkbox group:
clicking, tapping, or pressing the Space or Enter keys toggles selection for the focused row.
When the selectionBehavior
prop is set to replace
, clicking a row with the mouse replaces
the selection with only that row. Using the arrow keys moves both focus and selection.
To select multiple rows, modifier keys such as Ctrl, Cmd, and Shift can be used.
Rows Actions
Table supports rows via the onRowAction
callback. In the default toggle
selection
behavior, when nothing is selected, clicking or tapping the row triggers the row action.
This behavior is slightly different in the replace
selection behavior, where single
clicking selects the row and actions are performed via double click.
Sorting Rows
Table supports sorting its data when a column header is pressed. To designate that a Column
should support sorting, provide it with the allowsSorting
prop.
Table accepts a sortDescriptor
prop that defines the current column key to sort by
and the sort direction (ascending/descending). When the user presses a sortable column
header, the column's key and sort direction is passed into the onSortChange
callback,
allowing you to update the sortDescriptor
appropriately.
We recommend using the useAsyncList
hook from @react-stately/data to manage the data sorting. So make
sure to install it before using the sorting feature.
npm install @react-stately/data
import {useAsyncList} from "@react-stately/data";
Note that we passed the
isLoading
andloadingContent
props toTableBody
to render a loading state while the data is being fetched.
Loading more data
Table allows you to add a custom component at the end of the table, on the example below we are using a button to load more data.
Note: We passed the
isHeaderSticky
to theTable
component to make the header sticky.
Paginated Table
You can use the Pagination component to paginate the table.
Async Pagination
It is also possible to use the Pagination component to paginate the table asynchronously. To fetch the data, we are using the useSWR
hook from SWR.
Infinite Pagination
Table also supports infinite pagination. To do so, you can use the useAsyncList
hook from @react-stately/data and
@nextui-org/use-infinite-scroll hook.
npm install @react-stately/data @nextui-org/use-infinite-scroll
import { useInfiniteScroll } from "@nextui-org/use-infinite-scroll";import { useAsyncList } from "@react-stately/data";
Use Case Example
When creating a table, you usually need core functionalities like sorting, pagination, and filtering. In the example below, we combined all these functionalities to create a complete table.
Slots
- base: Defines a flexible column layout and relative positioning for the table component.
- wrapper: Applies to the outermost wrapper, providing padding, flexible layout, relative positioning, visual styles, and scrollable overflow handling.
- table: Sets the table to have a full minimum width and auto-adjusting height.
- thead: Specifies rounded corners for the first child row in the table header.
- tbody: No specific styles are applied to the body of the table.
- tr: Styles for table rows including group focus, outline properties, and a set of undefined focus-visible classes.
- th: Styles for table headers, including padding, text alignment, font properties, and special styles for sortable columns.
- td: Applies to table cells, with properties for padding, alignment, and relative positioning, plus special styles for first child elements, selection indication, and disabled cells.
- tfoot: No specific styles are applied to the footer of the table.
- sortIcon: Styles for sorting icons, with properties for margin, opacity, and transition effects based on sorting direction and hover state.
- emptyWrapper: Defines style for an empty table, with text alignment, color, and a specified height.
- loadingWrapper: Style applied when the table is loading, positioning it centrally in its container.
Custom Styles
You can customize the Table
component by passing custom Tailwind CSS classes to the component slots.
Data Attributes
TableBody
has the following attributes:
- data-empty: When the table is empty.
- data-loading:
When the table data is loading. Based on
TableBody
isLoading
andloadingContent
props.
TableRow
has the following attributes:
- data-selected:
When the row is selected. Based on
Table
selectedKeys
prop. - data-disabled:
When the row is disabled. Based on
Table
disabledKeys
prop. - data-hover: When the row is being hovered. Based on useHover
- data-focus-visible: When the row is being focused with the keyboard. Based on useFocusRing.
- data-first: When the row is the first row.
- data-middle: When the row is in the middle.
- data-odd: When the row is odd.
- data-last: When the row is the last row.
TableCell
has the following attributes:
- data-selected:
When the cell row is selected. Based on
Table
selectedKeys
prop. - data-focus-visible: When the cell is being focused with the keyboard. Based on useFocusRing.
Accessibility
- Exposed to assistive technology as a grid using ARIA.
- Keyboard navigation between columns, rows, cells, and in-cell focusable elements via the arrow keys.
- Single, multiple, or no row selection via mouse, touch, or keyboard interactions.
- Support for disabled rows, which cannot be selected.
- Column sorting support.
- Async loading, infinite scrolling, filtering, and sorting support.
- Support for both toggle and replace selection behaviors.
- Labeling support for accessibility.
- Ensures that selections are announced using an ARIA live region.
- Support for marking columns as row headers, which will be read when navigating the rows with a screen reader.
- Optional support for checkboxes in each row for selection, as well as in the header to select all rows.
- Automatic scrolling support during keyboard navigation.
- Support for row actions via double click, Enter key, or tapping.
- Typeahead to allow focusing rows by typing text.
- Long press to enter selection mode on touch when there is both selection and row actions.
API
Table Props
Prop | Type | Default |
children* |
| |
color |
| "default" |
layout |
| "auto" |
radius |
| "lg" |
shadow |
| "sm" |
hideHeader |
| false |
isStriped |
| false |
isCompact |
| false |
isHeaderSticky |
| false |
fullWidth |
| true |
removeWrapper |
| false |
BaseComponent |
| "div" |
topContent |
| |
bottomContent |
| |
topContentPlacement |
| "inside" |
bottomContentPlacement |
| "inside" |
showSelectionCheckboxes |
| |
sortDescriptor |
| |
selectedKeys |
| |
defaultSelectedKeys |
| |
disabledKeys |
| |
disallowEmptySelection |
| |
selectionMode |
| "none" |
selectionBehavior |
| "toggle" |
disabledBehavior |
| "selection" |
allowDuplicateSelectionEvents |
| |
disableAnimation |
| false |
checkboxesProps |
| |
classNames |
| |
isKeyboardNavigationDisabled |
| false |
Table Events
Prop | Type | Default |
onRowAction |
| |
onCellAction |
| |
onSelectionChange |
| |
onSortChange |
|
TableHeader Props
Prop | Type | Default |
children* |
| |
columns |
|
TableColumn Props
Prop | Type | Default |
children* |
| |
align |
| "start" |
hideHeader |
| false |
allowsSorting |
| |
isRowHeader |
| |
textValue |
| |
width |
| |
minWidth |
| |
maxWidth |
|
TableBody Props
Prop | Type | Default |
children* |
| |
items |
| |
isLoading |
| |
loadingState |
| |
loadingContent |
| |
emptyContent |
|
TableBody Events
Prop | Type | Default |
onLoadMore |
|
TableRow Props
Prop | Type | Default |
children* |
| |
textValue |
|
TableCell Props
Prop | Type | Default |
children* |
| |
textValue |
|
Table types
Sort descriptor
type SortDescriptor = {column: React.Key;direction: "ascending" | "descending";};
Selection
type Selection = "all" | Set<React.Key>;
Loading state
type LoadingState = "loading" | "sorting" | "loadingMore" | "error" | "idle" | "filtering";