Skip to content

shadcn.io is an independent project built on shadcn/ui

Free

Expandable Sub-Rows Data Table for React

Projects Website Redesign, Mobile App and API Integration. Each has a chevron that opens that project's tasks, plus Name and Status.

Three project rows, each with a chevron, a name and a status. Website Redesign is In Progress, Mobile App is Planning and API Integration is Completed. Tasks stay hidden until the chevron opens.

Expand a row when the detail belongs to that record, such as tasks. A second Table is clearer if the tasks are the main list. In React, Base UI does not invent the tasks.

Preview

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.
Use email insteadNo credit card

Usage

<div className="w-full max-w-4xl">
  <div className="rounded-md border bg-background">
    <Table>
      <TableHeader>
        {table.getHeaderGroups().map((headerGroup) => (
          <TableRow key={headerGroup.id}>
            {headerGroup.headers.map((header) => (
              <TableHead key={header.id}>
                {header.isPlaceholder ? null : (
                  <table.FlexRender header={header} />
                )}
              </TableHead>
            ))}
          </TableRow>
        ))}
      </TableHeader>
      <TableBody>
        {rows.map((row) => (
          <Fragment key={row.id}>
            <TableRow>
              {row.getAllCells().map((cell) => (
                <TableCell key={cell.id}>
                  <table.FlexRender cell={cell} />
                </TableCell>
              ))}
            </TableRow>
            {row.getIsExpanded() && row.original.tasks ? (
              <TableRow className="hover:bg-transparent">
                <TableCell colSpan={columns.length}>
                  <TaskList tasks={row.original.tasks} />
                </TableCell>
{/* The rest of the file is under Installation. */}

FAQ

How does a row expand?

getRowCanExpand is true when a project has tasks. The button calls row.getToggleExpandedHandler() and shows a down chevron while row.getIsExpanded() is true, a forward one otherwise. The forward chevron turns around in right-to-left layouts.

What is rendered under an open row?

A second TableRow whose single TableCell spans every column with colSpan. It holds a bg-muted/50 panel listing each task's name, assignee and status. The panel renders only while that row is expanded.

Which table features make expanding work?

tableFeatures includes rowExpandingFeature and expandedRowModel: createExpandedRowModel(). The tasks are a field on each project, not sub-rows, so the file passes no getSubRows.

Can a task expand again?

No. Tasks aren't table rows, so they have no expander. To nest rows, return the children from getSubRows and render the rows the expanded model returns.

Can I expand every row at once?

Call table.toggleAllRowsExpanded(), for example from a button in the expander column's header. This demo opens rows one at a time, and they all start closed.

Does the expander button have an accessible name?

Yes. It's labeled Show tasks or Hide tasks and sets aria-expanded, so a screen reader says whether the panel is open.

API Reference

Table

Sets colSpan={columns.length}. The rest is on the Table page.