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.


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.



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 to render the items, but it will not be as performant as using the items and columns prop.


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">
{ =>
<TableColumn key={column.key}>{column.label}</TableColumn>
{ =>
<TableRow key={row.key}>
{(columnKey) => <TableCell>{getKeyValue(row, columnKey)}</TableCell>}

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 selectioMode 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 an Set 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.

import {useAsyncList} from "@react-stately/data";

Note that we passed the isLoading and loadingContent props to TableBody 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 the Table 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.

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.


  • 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 and loadingContent 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.


  • 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.


Table Props

children*ReactNode[]The elements that make up the table. Includes the TableHeader, TableBody, TableColumn, and TableRow.-
colordefault | primary | secondary | success | warning | dangerColor of the selected rows, and checkboxes.default
layoutauto | fixedDefines the layout of the
radiusnone | sm | md | lgThe border-radius of the table.lg
shadownone | sm | md | lgThe shadow size of the
hideHeaderbooleanWhether to hide the table header.false
isStripedbooleanWhether to apply striped rows in the table.false
isCompactbooleanWhether to apply compact style to the table.false
isHeaderStickybooleanWhether to make the table header sticky.false
fullWidthbooleanWhether to make the table full width.true
disableAnimationbooleanWhether to disable animations in the table.false
removeWrapperbooleanWhether the table base container should not be rendered.false
BaseComponentReact.ComponentType<any>A custom wrapper component for the table.div
topContentReactNodeProvides content to include a component in the top of the table.-
bottomContentReactNodeProvides content to include a component in the bottom of the table.-
topContentPlacementinside | outsideWhere to place the topContent component.inside
bottomContentPlacementinside | outsideWhere to place the bottomContent component.inside
showSelectionCheckboxesbooleanWhether the row selection checkboxes should be displayed.-
sortDescriptorSortDescriptorThe current sorted column and direction.-
selectedKeysSelectionThe currently selected keys in the collection (controlled).-
defaultSelectedKeysSelectionThe initial selected keys in the collection (uncontrolled).-
disabledKeysSelectionA set of keys for rows that are disabled.-
disallowEmptySelectionbooleanWhether the collection allows empty selection.-
selectionModesingle | multiple | noneThe type of selection that is allowed in the collection.none
selectionBehaviortoggle | replaceHow multiple selection should behave in the collection.toggle
disabledBehaviorselection | allWhether disabledKeys applies to all interactions, or only selection.selection
allowDuplicateSelectionEventsbooleanWhether onSelectionChange should fire even if the new set of keys is the same as the last.-
disableAnimationbooleanWhether to disable the table and checkbox animations.false
checkboxesPropsCheckboxPropsProps to be passed to the checkboxes.-
classNamesRecord<"base" | "table" | "thead" | "tbody" | "tfoot" | "emptyWrapper" | "loadingWrapper" | "wrapper" | "tr" | "th" | "td" | "sortIcon", string>Allows to set custom class names for the dropdown item slots.-

Table Events

onRowAction(key: React.Key) => voidHandler that is called when a user performs an action on the row.
onCellAction(key: react.Key) => voidHandler that is called when a user performs an action on the cell.
onSelectionChange(keys: Selection) => anyHandler that is called when the selection changes.
onSortChange(descriptor: SortDescriptor) => anyHandler that is called when the sorted column or direction changes.

TableHeader Props

children*ReactNode[]A list of Column(s) or a function. If the latter, a list of columns must be provided using the columns prop-
columnsT[]A list of table columns.-

TableColumn Props

children*ReactNodeStatic child columns or content to render as the column header-
alignstart | center | endThe alignment of the column's contents relative to its allotted widthstart
hideHeaderbooleanWhether the column should hide its header textfalse
allowsSortingbooleanWhether the column allows sorting-
isRowHeaderbooleanWhether a column is a row header and should be announced by assistive technology during row navigation-
textValuestringA string representation of the column's contents, used for accessibility announcements-
widthstring | numberThe width of the column-
minWidthstring | numberThe minimum width of the column-
maxWidthstring | numberThe maximum width of the column-

TableBody Props

children*RowElement | RowElement[] | ((item: T) => RowElement)The contents of the table body. Supports static items or a function for dynamic rendering-
itemsIterable<T>A list of row objects in the table body used when dynamically rendering rows-
isLoadingbooleanWhether the table body is loading.-
loadingStateLoadingStateHandler that is called when more items should be loaded, e.g. while scrolling near the bottom-
loadingContentReactNodeContent to display while loading more items-
emptyContentReactNodeContent to display when there are no items in the table body-

TableBody Events

onLoadMore() => anyA list of row objects in the table body used when dynamically rendering rows

TableRow Props

children*CellElement | CellElement[] | CellRendererRendered contents of the row or row child items-
textValuestringA string representation of the row's contents, used for features like typeahead-

TableCell Props

children*ReactNodeThe contents of the cell-
textValuestringA string representation of the row's contents, used for features like typeahead-

Table types

Sort descriptor

type SortDescriptor = {
column: React.Key;
direction: "ascending" | "descending";


type Selection = "all" | Set<React.Key>;

Loading state

type LoadingState = "loading" | "sorting" | "loadingMore" | "error" | "idle" | "filtering";