Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Components

Free

Table for React

Compare rows such as invoices or payments, with the values lined up in columns. For React with Base UI, sorting is not included, and wide tables scroll sideways.

Framework
UI library

Rows and columns for data people compare, like invoices or payments. The header looks clickable, but sorting is not built in.

Use a table when people scan values down a column. A handful of labeled fields, not a grid, is a Data List. In React, Base UI supplies the table styles, and you render the rows yourself.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Anatomy

Table anatomy: header, head, row, cell, footer, caption

Usage

import {
  Table, TableBody, TableCaption, TableCell, TableFooter, TableHead,
  TableHeader, TableRow,
} from "@/components/ui/table";
const invoices = [
  {
    invoice: "INV001",
    paymentMethod: "Credit Card",
    paymentStatus: "Paid",
    totalAmount: "$250.00",
  },
  {
    invoice: "INV002",
    paymentMethod: "PayPal",
    paymentStatus: "Pending",
    totalAmount: "$150.00",
  },
  {
    invoice: "INV003",
    paymentMethod: "Bank Transfer",
    paymentStatus: "Unpaid",
    totalAmount: "$350.00",
  },
  {
    invoice: "INV004",
    paymentMethod: "Credit Card",
    paymentStatus: "Paid",
    totalAmount: "$450.00",
  },
  {
    invoice: "INV005",
    paymentMethod: "PayPal",
    paymentStatus: "Paid",
    totalAmount: "$550.00",
  },
  {
    invoice: "INV006",
    paymentMethod: "Bank Transfer",
    paymentStatus: "Pending",
    totalAmount: "$200.00",
  },
  {
    invoice: "INV007",
    paymentMethod: "Credit Card",
    paymentStatus: "Unpaid",
    totalAmount: "$300.00",
  },
];

const TableDemo = () => (
  <Table>
    <TableCaption>A list of your recent invoices.</TableCaption>
    <TableHeader>
      <TableRow>
        <TableHead className="w-[100px]">Invoice</TableHead>
        <TableHead>Status</TableHead>
        <TableHead>Method</TableHead>
        <TableHead className="text-right">Amount</TableHead>
      </TableRow>
    </TableHeader>
    <TableBody>
      {invoices.map((invoice) => (
        <TableRow key={invoice.invoice}>
          <TableCell className="font-medium">{invoice.invoice}</TableCell>
          <TableCell>{invoice.paymentStatus}</TableCell>
          <TableCell>{invoice.paymentMethod}</TableCell>
          <TableCell className="text-right">{invoice.totalAmount}</TableCell>
        </TableRow>
      ))}
    </TableBody>
    <TableFooter>
      <TableRow>
        <TableCell colSpan={3}>Total</TableCell>
        <TableCell className="text-right">$2,500.00</TableCell>
      </TableRow>
    </TableFooter>
  </Table>
);

Demos

TableFooter holds a summary row, a Total label spanning the first columns and the amount in the last cell. The body above it is a few invoices, so the total is not another record in disguise. Use a footer for a sum, a count, or a balance that should look different from the data, and keep real records in TableBody. A footer that repeats the column headers does not help.

Preview width

Actions

Each product row ends in a menu: Edit, Duplicate, and a destructive Delete. The trigger is an icon button named Open menu, so it is not an unlabeled ellipsis. Use a per-row menu when the actions belong to that one record and would not fit as buttons in the cell. Actions on many selected rows belong in an Action Bar, and this column should stay narrow.

Preview width

Data Table

The same cells, with TanStack Table behind them: filter the email column, sort it, hide columns, select rows, and page five at a time. The grid is still just the cells, and the table library owns the state. Use this shape for an admin list that needs those behaviors, not for three static invoices. The footer and the row menu above are the smaller patterns.

Preview width

Rtl

dir is rtl on the table, with an Arabic caption, headers, and statuses, and the amount stays in the last column. Use it when the table's language is right-to-left. Setting dir here does not flip the rest of the page. Set direction on the document when every region should follow, and on the table when only this grid is right-to-left.

Preview width

FAQ

How do I add sorting, filtering and pagination to the Table in React?

The Table only styles rows and cells, so sort, filter and page your data before you render it. For a full data table, see the Data Table demo: it pairs the Table with TanStack Table for sorting, filtering, column visibility, row selection and pagination.

How do I make the Table responsive on mobile?

The Table sits in a container that scrolls sideways when the columns do not fit, so wide tables stay readable on small screens. Keep the first column short, and hide less important columns with classes like hidden md:table-cell. To let a keyboard scroll it, give the Table tabIndex={0} and a name (aria-label or aria-labelledby): the container takes the tab stop and becomes a region with the table's name.

How do I make a striped Table with Base UI?

Add a class to the body rows, for example even:bg-muted/30 on TableRow. Rows highlight on hover; edit the Table file in your ui folder to change that.

How do I add an actions menu to each Table row?

Put a button in the last TableCell of each row that opens a Dropdown Menu, as the Actions demo does. Give the button an aria-label that names the row, like "Actions for Ada".

Can I select rows in the Table?

Yes: keep the selected rows in state and add a Checkbox to a first column, as the Data Table demo does with TanStack Table.

How do I install the Table in React?

Run the command in the Installation section above: it adds the Table and its parts to your ui folder. The command uses the shadcn CLI.

API Reference

Table

<div> data-slot="table-container"

PropType
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

TableHeader

<thead> data-slot="table-header"

PropType
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

TableBody

<tbody> data-slot="table-body"

PropType
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

TableFooter

<tfoot> data-slot="table-footer"

PropType
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

TableRow

<tr> data-slot="table-row"

PropType
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

TableHead

<th> data-slot="table-head"

PropType
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

TableCell

<td> data-slot="table-cell"

PropType
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

TableCaption

<caption> data-slot="table-caption"

PropType
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

With other components

  • Free

    Empty

    Feedback

    No rows to show

  • Free

    Card

    Layout

    Frame around the table

  • Free

    Chart

    Data

    Same numbers, plotted

  • Free

    Item

    Layout

    One row, not columns

  • Free

    Pairs, not a grid

  • Free

    Action Bar

    Overlays

    Actions for the selection