Table

Data tables are the primary mechanism for organizing and displaying complex sets of data. They allow users to scan, sort, filter, and compare information efficiently.

Primary use cases:

  • Managing large lists (e.g., Artist roster, Campaign plans, User management, Artist's tracks).
  • Comparing metrics across multiple entities (e.g., Track performance).
  • Reviewing line-item details (e.g., Transaction history).

Table is built on top of TanStack Table.

All Tables are rendered with TanStack Table and the following columns and data defaults:

tsx
const columns = [
  columnHelper.accessor('name', {
    id: 'name',
    header: 'Name',
  }),
  columnHelper.accessor('email', {
    id: 'email',
    header: 'Email',
  }),
];

const data = [
  { id: '1', name: 'John Doe', email: 'john@example.com' },
  { id: '2', name: 'Jane Doe', email: 'jane@example.com' },
];

Basic Table

A barebones table containing text data.

Name Email
John Doe john@example.com
Jane Doe jane@example.com
Code snippet
Package name <Table> Storybook source
tsx
<Table
  columns={[
    {
      accessorKey: 'name',
      enableSorting: false,
      header: 'Name',
      id: 'name'
    },
    {
      accessorKey: 'email',
      enableSorting: false,
      header: 'Email',
      id: 'email'
    }
  ]}
  data={[
    {
      email: 'john@example.com',
      id: '1',
      name: 'John Doe'
    },
    {
      email: 'jane@example.com',
      id: '2',
      name: 'Jane Doe'
    }
  ]}
  onSortingChange={function f0e(){}}
/>

Striped Table

Most use cases will likely set variant to "striped" to improve readability.

Name Email
John Doe john@example.com
Jane Doe jane@example.com
Code snippet
Package name <Table> Storybook source
tsx
<Table
  columns={[
    {
      accessorKey: 'name',
      enableSorting: false,
      header: 'Name',
      id: 'name'
    },
    {
      accessorKey: 'email',
      enableSorting: false,
      header: 'Email',
      id: 'email'
    }
  ]}
  data={[
    {
      email: 'john@example.com',
      id: '1',
      name: 'John Doe'
    },
    {
      email: 'jane@example.com',
      id: '2',
      name: 'Jane Doe'
    }
  ]}
  onSortingChange={function f0e(){}}
  variant="striped"
/>

Loading Skeletons

Pass isLoading as true to show the default loading skeletons for Table. Customize how many rows show up with loadingRowCount.

Name Email
Code snippet
Package name <Table> Storybook source
tsx
<Table
  columns={[
    {
      accessorKey: 'name',
      enableSorting: false,
      header: 'Name',
      id: 'name'
    },
    {
      accessorKey: 'email',
      enableSorting: false,
      header: 'Email',
      id: 'email'
    }
  ]}
  data={[]}
  isLoading
  loadingRowCount={3}
  onSortingChange={function f0e(){}}
/>

Displaying No Content

By default, a table with no content will show a single row saying "No data".

Name Email
No data
Code snippet
Package name <Table> Storybook source
tsx
<Table
  columns={[
    {
      accessorKey: 'name',
      enableSorting: false,
      header: 'Name',
      id: 'name'
    },
    {
      accessorKey: 'email',
      enableSorting: false,
      header: 'Email',
      id: 'email'
    }
  ]}
  data={[]}
  onSortingChange={function f0e(){}}
/>

Customize this content by passing in a component to EmptyContent.

Name Email
Empty
Code snippet
Package name <Table> Storybook source
tsx
<Table
  EmptyContent={function f0e(){}}
  columns={[
    {
      accessorKey: 'name',
      enableSorting: false,
      header: 'Name',
      id: 'name'
    },
    {
      accessorKey: 'email',
      enableSorting: false,
      header: 'Email',
      id: 'email'
    }
  ]}
  data={[]}
  onSortingChange={function f0e(){}}
/>

Sorting

The table headers support showing sorting information on columns where enableSorting is set to true.

Sortable (unsorted)

Name Email
John Doe john@example.com
Jane Doe jane@example.com
Code snippet
Package name <Table> Storybook source
tsx
<Table
  columns={[
    {
      accessorKey: 'name',
      enableSorting: true,
      header: 'Name',
      id: 'name',
      sortingFn: 'alphanumeric'
    },
    {
      accessorKey: 'email',
      enableSorting: true,
      header: 'Email',
      id: 'email',
      sortingFn: 'alphanumeric'
    }
  ]}
  data={[
    {
      email: 'john@example.com',
      id: '1',
      name: 'John Doe'
    },
    {
      email: 'jane@example.com',
      id: '2',
      name: 'Jane Doe'
    }
  ]}
  onSortingChange={function f0e(){}}
/>

Sorted by Name — Ascending

Name Email
Jane Doe jane@example.com
John Doe john@example.com
Code snippet
Package name <Table> Storybook source
tsx
<Table
  columns={[
    {
      accessorKey: 'name',
      enableSorting: true,
      header: 'Name',
      id: 'name',
      sortingFn: 'alphanumeric'
    },
    {
      accessorKey: 'email',
      enableSorting: true,
      header: 'Email',
      id: 'email',
      sortingFn: 'alphanumeric'
    }
  ]}
  data={[
    {
      email: 'jane@example.com',
      id: '2',
      name: 'Jane Doe'
    },
    {
      email: 'john@example.com',
      id: '1',
      name: 'John Doe'
    }
  ]}
  onSortingChange={function f0e(){}}
  sort={[{desc: false, id: 'name'}]}
/>

Sorted by Name — Descending

Name Email
John Doe john@example.com
Jane Doe jane@example.com
Code snippet
Package name <Table> Storybook source
tsx
<Table
  columns={[
    {
      accessorKey: 'name',
      enableSorting: true,
      header: 'Name',
      id: 'name',
      sortingFn: 'alphanumeric'
    },
    {
      accessorKey: 'email',
      enableSorting: true,
      header: 'Email',
      id: 'email',
      sortingFn: 'alphanumeric'
    }
  ]}
  data={[
    {
      email: 'john@example.com',
      id: '1',
      name: 'John Doe'
    },
    {
      email: 'jane@example.com',
      id: '2',
      name: 'Jane Doe'
    }
  ]}
  onSortingChange={function f0e(){}}
  sort={[{desc: true, id: 'name'}]}
/>

Sorting can happen client-side or server-side. Most sorting is expected to be handled server-side. Use the manualSorting, onSortingChange, and sorting props to control the sorting state of the Table.

Using the Component Parts Individually

The Table component uses a bunch of pieces under the hood. You can build out your own table using those pieces to get significantly more flexibility at the cost of manual maintenance.

Name Email
John Doe john@example.com
Jane Doe jane@example.com
Code snippet
Package name <Table> Storybook source
tsx
{
  render() {
    const table = useReactTable({
      data: [{
        id: '1',
        name: 'John Doe',
        email: 'john@example.com'
      }, {
        id: '2',
        name: 'Jane Doe',
        email: 'jane@example.com'
      }],
      columns,
      getCoreRowModel: getCoreRowModel()
    });
    return <Table.Table table={table}>
        <Table.Head>
          {table.getHeaderGroups().map(headerGroup => <Table.Row key={headerGroup.id}>
              {headerGroup.headers.map(header => <Table.HeadCell key={header.id} header={header} />)}
            </Table.Row>)}
        </Table.Head>
        <Table.Body>
          <Table.Row>
            <Table.Cell>John Doe</Table.Cell>
            <Table.Cell>john@example.com</Table.Cell>
          </Table.Row>
          <Table.Row>
            <Table.Cell>Jane Doe</Table.Cell>
            <Table.Cell>jane@example.com</Table.Cell>
          </Table.Row>
        </Table.Body>
      </Table.Table>;
  }
}