mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-15 20:01:35 +00:00
871 lines
21 KiB
Plaintext
871 lines
21 KiB
Plaintext
---
|
|
title: Data Table
|
|
description: Powerful table and datagrids built using TanStack Table.
|
|
component: true
|
|
links:
|
|
doc: https://tanstack.com/table/v8/docs/introduction
|
|
---
|
|
|
|
<ComponentPreview name="data-table-demo" className="[&_.preview]:items-start" />
|
|
|
|
## Introduction
|
|
|
|
Every data table or datagrid I've created has been unique. They all behave differently, have specific sorting and filtering requirements, and work with different data sources.
|
|
|
|
It doesn't make sense to combine all of these variations into a single component. If we do that, we'll lose the flexibility that [headless UI](https://tanstack.com/table/v8/docs/introduction#what-is-headless-ui) provides.
|
|
|
|
So instead of a data-table component, I thought it would be more helpful to provide a guide on how to build your own.
|
|
|
|
We'll start with the basic `<Table />` component and build a complex data table from scratch.
|
|
|
|
<Callout className="mt-4">
|
|
|
|
**Tip:** If you find yourself using the same table in multiple places in your app, you can always extract it into a reusable component.
|
|
|
|
</Callout>
|
|
|
|
## Table of Contents
|
|
|
|
This guide will show you how to use [TanStack Table](https://tanstack.com/table) and the `<Table />` component to build your own custom data table. We'll cover the following topics:
|
|
|
|
- [Basic Table](#basic-table)
|
|
- [Row Actions](#row-actions)
|
|
- [Pagination](#pagination)
|
|
- [Sorting](#sorting)
|
|
- [Filtering](#filtering)
|
|
- [Visibility](#visibility)
|
|
- [Row Selection](#row-selection)
|
|
- [Reusable Components](#reusable-components)
|
|
|
|
## Installation
|
|
|
|
1. Add the `<Table />` component to your project:
|
|
|
|
```bash
|
|
npx shadcn@latest add table
|
|
```
|
|
|
|
2. Add `tanstack/react-table` dependency:
|
|
|
|
```bash
|
|
npm install @tanstack/react-table
|
|
```
|
|
|
|
## Prerequisites
|
|
|
|
We are going to build a table to show recent payments. Here's what our data looks like:
|
|
|
|
```tsx showLineNumbers
|
|
type Payment = {
|
|
id: string
|
|
amount: number
|
|
status: "pending" | "processing" | "success" | "failed"
|
|
email: string
|
|
}
|
|
|
|
export const payments: Payment[] = [
|
|
{
|
|
id: "728ed52f",
|
|
amount: 100,
|
|
status: "pending",
|
|
email: "m@example.com",
|
|
},
|
|
{
|
|
id: "489e1d42",
|
|
amount: 125,
|
|
status: "processing",
|
|
email: "example@gmail.com",
|
|
},
|
|
// ...
|
|
]
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
Start by creating the following file structure:
|
|
|
|
```txt
|
|
app
|
|
└── payments
|
|
├── columns.tsx
|
|
├── data-table.tsx
|
|
└── page.tsx
|
|
```
|
|
|
|
I'm using a Next.js example here but this works for any other React framework.
|
|
|
|
- `columns.tsx` (client component) will contain our column definitions.
|
|
- `data-table.tsx` (client component) will contain our `<DataTable />` component.
|
|
- `page.tsx` (server component) is where we'll fetch data and render our table.
|
|
|
|
## Basic Table
|
|
|
|
Let's start by building a basic table.
|
|
|
|
<Steps>
|
|
|
|
### Column Definitions
|
|
|
|
First, we'll define our columns.
|
|
|
|
```tsx showLineNumbers title="app/payments/columns.tsx" {3,14-27}
|
|
"use client"
|
|
|
|
import { ColumnDef } from "@tanstack/react-table"
|
|
|
|
// This type is used to define the shape of our data.
|
|
// You can use a Zod schema here if you want.
|
|
export type Payment = {
|
|
id: string
|
|
amount: number
|
|
status: "pending" | "processing" | "success" | "failed"
|
|
email: string
|
|
}
|
|
|
|
export const columns: ColumnDef<Payment>[] = [
|
|
{
|
|
accessorKey: "status",
|
|
header: "Status",
|
|
},
|
|
{
|
|
accessorKey: "email",
|
|
header: "Email",
|
|
},
|
|
{
|
|
accessorKey: "amount",
|
|
header: "Amount",
|
|
},
|
|
]
|
|
```
|
|
|
|
<Callout className="mt-4">
|
|
|
|
**Note:** Columns are where you define the core of what your table
|
|
will look like. They define the data that will be displayed, how it will be
|
|
formatted, sorted and filtered.
|
|
|
|
</Callout>
|
|
|
|
### `<DataTable />` component
|
|
|
|
Next, we'll create a `<DataTable />` component to render our table.
|
|
|
|
```tsx showLineNumbers title="app/payments/data-table.tsx"
|
|
"use client"
|
|
|
|
import {
|
|
ColumnDef,
|
|
flexRender,
|
|
getCoreRowModel,
|
|
useReactTable,
|
|
} from "@tanstack/react-table"
|
|
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "@/components/ui/table"
|
|
|
|
interface DataTableProps<TData, TValue> {
|
|
columns: ColumnDef<TData, TValue>[]
|
|
data: TData[]
|
|
}
|
|
|
|
export function DataTable<TData, TValue>({
|
|
columns,
|
|
data,
|
|
}: DataTableProps<TData, TValue>) {
|
|
const table = useReactTable({
|
|
data,
|
|
columns,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
})
|
|
|
|
return (
|
|
<div className="rounded-md border">
|
|
<Table>
|
|
<TableHeader>
|
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
<TableRow key={headerGroup.id}>
|
|
{headerGroup.headers.map((header) => {
|
|
return (
|
|
<TableHead key={header.id}>
|
|
{header.isPlaceholder
|
|
? null
|
|
: flexRender(
|
|
header.column.columnDef.header,
|
|
header.getContext()
|
|
)}
|
|
</TableHead>
|
|
)
|
|
})}
|
|
</TableRow>
|
|
))}
|
|
</TableHeader>
|
|
<TableBody>
|
|
{table.getRowModel().rows?.length ? (
|
|
table.getRowModel().rows.map((row) => (
|
|
<TableRow
|
|
key={row.id}
|
|
data-state={row.getIsSelected() && "selected"}
|
|
>
|
|
{row.getVisibleCells().map((cell) => (
|
|
<TableCell key={cell.id}>
|
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
</TableCell>
|
|
))}
|
|
</TableRow>
|
|
))
|
|
) : (
|
|
<TableRow>
|
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
No results.
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
<Callout>
|
|
|
|
**Tip**: If you find yourself using `<DataTable />` in multiple places, this is the component you could make reusable by extracting it to `components/ui/data-table.tsx`.
|
|
|
|
`<DataTable columns={columns} data={data} />`
|
|
|
|
</Callout>
|
|
|
|
### Render the table
|
|
|
|
Finally, we'll render our table in our page component.
|
|
|
|
```tsx showLineNumbers title="app/payments/page.tsx" {22}
|
|
import { Payment, columns } from "./columns"
|
|
import { DataTable } from "./data-table"
|
|
|
|
async function getData(): Promise<Payment[]> {
|
|
// Fetch data from your API here.
|
|
return [
|
|
{
|
|
id: "728ed52f",
|
|
amount: 100,
|
|
status: "pending",
|
|
email: "m@example.com",
|
|
},
|
|
// ...
|
|
]
|
|
}
|
|
|
|
export default async function DemoPage() {
|
|
const data = await getData()
|
|
|
|
return (
|
|
<div className="container mx-auto py-10">
|
|
<DataTable columns={columns} data={data} />
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
</Steps>
|
|
|
|
## Cell Formatting
|
|
|
|
Let's format the amount cell to display the dollar amount. We'll also align the cell to the right.
|
|
|
|
<Steps>
|
|
|
|
### Update columns definition
|
|
|
|
Update the `header` and `cell` definitions for amount as follows:
|
|
|
|
```tsx showLineNumbers title="app/payments/columns.tsx" {4-15}
|
|
export const columns: ColumnDef<Payment>[] = [
|
|
{
|
|
accessorKey: "amount",
|
|
header: () => <div className="text-right">Amount</div>,
|
|
cell: ({ row }) => {
|
|
const amount = parseFloat(row.getValue("amount"))
|
|
const formatted = new Intl.NumberFormat("en-US", {
|
|
style: "currency",
|
|
currency: "USD",
|
|
}).format(amount)
|
|
|
|
return <div className="text-right font-medium">{formatted}</div>
|
|
},
|
|
},
|
|
]
|
|
```
|
|
|
|
You can use the same approach to format other cells and headers.
|
|
|
|
</Steps>
|
|
|
|
## Row Actions
|
|
|
|
Let's add row actions to our table. We'll use a `<Dropdown />` component for this.
|
|
|
|
<Steps>
|
|
|
|
### Update columns definition
|
|
|
|
Update our columns definition to add a new `actions` column. The `actions` cell returns a `<Dropdown />` component.
|
|
|
|
```tsx showLineNumbers title="app/payments/columns.tsx" {4,6-14,18-45}
|
|
"use client"
|
|
|
|
import { ColumnDef } from "@tanstack/react-table"
|
|
import { MoreHorizontal } from "lucide-react"
|
|
|
|
import { Button } from "@/components/ui/button"
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu"
|
|
|
|
export const columns: ColumnDef<Payment>[] = [
|
|
// ...
|
|
{
|
|
id: "actions",
|
|
cell: ({ row }) => {
|
|
const payment = row.original
|
|
|
|
return (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" className="h-8 w-8 p-0">
|
|
<span className="sr-only">Open menu</span>
|
|
<MoreHorizontal className="h-4 w-4" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
|
<DropdownMenuItem
|
|
onClick={() => navigator.clipboard.writeText(payment.id)}
|
|
>
|
|
Copy payment ID
|
|
</DropdownMenuItem>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem>View customer</DropdownMenuItem>
|
|
<DropdownMenuItem>View payment details</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
)
|
|
},
|
|
},
|
|
// ...
|
|
]
|
|
```
|
|
|
|
You can access the row data using `row.original` in the `cell` function. Use this to handle actions for your row eg. use the `id` to make a DELETE call to your API.
|
|
|
|
</Steps>
|
|
|
|
## Pagination
|
|
|
|
Next, we'll add pagination to our table.
|
|
|
|
<Steps>
|
|
|
|
### Update `<DataTable>`
|
|
|
|
```tsx showLineNumbers title="app/payments/data-table.tsx" {5,17}
|
|
import {
|
|
ColumnDef,
|
|
flexRender,
|
|
getCoreRowModel,
|
|
getPaginationRowModel,
|
|
useReactTable,
|
|
} from "@tanstack/react-table"
|
|
|
|
export function DataTable<TData, TValue>({
|
|
columns,
|
|
data,
|
|
}: DataTableProps<TData, TValue>) {
|
|
const table = useReactTable({
|
|
data,
|
|
columns,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getPaginationRowModel: getPaginationRowModel(),
|
|
})
|
|
|
|
// ...
|
|
}
|
|
```
|
|
|
|
This will automatically paginate your rows into pages of 10. See the [pagination docs](https://tanstack.com/table/v8/docs/api/features/pagination) for more information on customizing page size and implementing manual pagination.
|
|
|
|
### Add pagination controls
|
|
|
|
We can add pagination controls to our table using the `<Button />` component and the `table.previousPage()`, `table.nextPage()` API methods.
|
|
|
|
```tsx showLineNumbers title="app/payments/data-table.tsx" {1,15,21-39}
|
|
import { Button } from "@/components/ui/button"
|
|
|
|
export function DataTable<TData, TValue>({
|
|
columns,
|
|
data,
|
|
}: DataTableProps<TData, TValue>) {
|
|
const table = useReactTable({
|
|
data,
|
|
columns,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getPaginationRowModel: getPaginationRowModel(),
|
|
})
|
|
|
|
return (
|
|
<div>
|
|
<div className="rounded-md border">
|
|
<Table>
|
|
{ // .... }
|
|
</Table>
|
|
</div>
|
|
<div className="flex items-center justify-end space-x-2 py-4">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => table.previousPage()}
|
|
disabled={!table.getCanPreviousPage()}
|
|
>
|
|
Previous
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => table.nextPage()}
|
|
disabled={!table.getCanNextPage()}
|
|
>
|
|
Next
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
See [Reusable Components](#reusable-components) section for a more advanced pagination component.
|
|
|
|
</Steps>
|
|
|
|
## Sorting
|
|
|
|
Let's make the email column sortable.
|
|
|
|
<Steps>
|
|
|
|
### Update `<DataTable>`
|
|
|
|
```tsx showLineNumbers title="app/payments/data-table.tsx" showLineNumbers {3,6,10,18,25-28}
|
|
"use client"
|
|
|
|
import * as React from "react"
|
|
import {
|
|
ColumnDef,
|
|
SortingState,
|
|
flexRender,
|
|
getCoreRowModel,
|
|
getPaginationRowModel,
|
|
getSortedRowModel,
|
|
useReactTable,
|
|
} from "@tanstack/react-table"
|
|
|
|
export function DataTable<TData, TValue>({
|
|
columns,
|
|
data,
|
|
}: DataTableProps<TData, TValue>) {
|
|
const [sorting, setSorting] = React.useState<SortingState>([])
|
|
|
|
const table = useReactTable({
|
|
data,
|
|
columns,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getPaginationRowModel: getPaginationRowModel(),
|
|
onSortingChange: setSorting,
|
|
getSortedRowModel: getSortedRowModel(),
|
|
state: {
|
|
sorting,
|
|
},
|
|
})
|
|
|
|
return (
|
|
<div>
|
|
<div className="rounded-md border">
|
|
<Table>{ ... }</Table>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
### Make header cell sortable
|
|
|
|
We can now update the `email` header cell to add sorting controls.
|
|
|
|
```tsx showLineNumbers title="app/payments/columns.tsx" {4,9-19}
|
|
"use client"
|
|
|
|
import { ColumnDef } from "@tanstack/react-table"
|
|
import { ArrowUpDown } from "lucide-react"
|
|
|
|
export const columns: ColumnDef<Payment>[] = [
|
|
{
|
|
accessorKey: "email",
|
|
header: ({ column }) => {
|
|
return (
|
|
<Button
|
|
variant="ghost"
|
|
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
|
>
|
|
Email
|
|
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
</Button>
|
|
)
|
|
},
|
|
},
|
|
]
|
|
```
|
|
|
|
This will automatically sort the table (asc and desc) when the user toggles on the header cell.
|
|
|
|
</Steps>
|
|
|
|
## Filtering
|
|
|
|
Let's add a search input to filter emails in our table.
|
|
|
|
<Steps>
|
|
|
|
### Update `<DataTable>`
|
|
|
|
```tsx showLineNumbers title="app/payments/data-table.tsx" {6,10,17,24-26,35-36,39,45-54}
|
|
"use client"
|
|
|
|
import * as React from "react"
|
|
import {
|
|
ColumnDef,
|
|
ColumnFiltersState,
|
|
SortingState,
|
|
flexRender,
|
|
getCoreRowModel,
|
|
getFilteredRowModel,
|
|
getPaginationRowModel,
|
|
getSortedRowModel,
|
|
useReactTable,
|
|
} from "@tanstack/react-table"
|
|
|
|
import { Button } from "@/components/ui/button"
|
|
import { Input } from "@/components/ui/input"
|
|
|
|
export function DataTable<TData, TValue>({
|
|
columns,
|
|
data,
|
|
}: DataTableProps<TData, TValue>) {
|
|
const [sorting, setSorting] = React.useState<SortingState>([])
|
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
[]
|
|
)
|
|
|
|
const table = useReactTable({
|
|
data,
|
|
columns,
|
|
onSortingChange: setSorting,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getPaginationRowModel: getPaginationRowModel(),
|
|
getSortedRowModel: getSortedRowModel(),
|
|
onColumnFiltersChange: setColumnFilters,
|
|
getFilteredRowModel: getFilteredRowModel(),
|
|
state: {
|
|
sorting,
|
|
columnFilters,
|
|
},
|
|
})
|
|
|
|
return (
|
|
<div>
|
|
<div className="flex items-center py-4">
|
|
<Input
|
|
placeholder="Filter emails..."
|
|
value={(table.getColumn("email")?.getFilterValue() as string) ?? ""}
|
|
onChange={(event) =>
|
|
table.getColumn("email")?.setFilterValue(event.target.value)
|
|
}
|
|
className="max-w-sm"
|
|
/>
|
|
</div>
|
|
<div className="rounded-md border">
|
|
<Table>{ ... }</Table>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
Filtering is now enabled for the `email` column. You can add filters to other columns as well. See the [filtering docs](https://tanstack.com/table/v8/docs/guide/filters) for more information on customizing filters.
|
|
|
|
</Steps>
|
|
|
|
## Visibility
|
|
|
|
Adding column visibility is fairly simple using `@tanstack/react-table` visibility API.
|
|
|
|
<Steps>
|
|
|
|
### Update `<DataTable>`
|
|
|
|
```tsx showLineNumbers title="app/payments/data-table.tsx" {8,18-23,33-34,45,49,64-91}
|
|
"use client"
|
|
|
|
import * as React from "react"
|
|
import {
|
|
ColumnDef,
|
|
ColumnFiltersState,
|
|
SortingState,
|
|
VisibilityState,
|
|
flexRender,
|
|
getCoreRowModel,
|
|
getFilteredRowModel,
|
|
getPaginationRowModel,
|
|
getSortedRowModel,
|
|
useReactTable,
|
|
} from "@tanstack/react-table"
|
|
|
|
import { Button } from "@/components/ui/button"
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuCheckboxItem,
|
|
DropdownMenuContent,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu"
|
|
|
|
export function DataTable<TData, TValue>({
|
|
columns,
|
|
data,
|
|
}: DataTableProps<TData, TValue>) {
|
|
const [sorting, setSorting] = React.useState<SortingState>([])
|
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
[]
|
|
)
|
|
const [columnVisibility, setColumnVisibility] =
|
|
React.useState<VisibilityState>({})
|
|
|
|
const table = useReactTable({
|
|
data,
|
|
columns,
|
|
onSortingChange: setSorting,
|
|
onColumnFiltersChange: setColumnFilters,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getPaginationRowModel: getPaginationRowModel(),
|
|
getSortedRowModel: getSortedRowModel(),
|
|
getFilteredRowModel: getFilteredRowModel(),
|
|
onColumnVisibilityChange: setColumnVisibility,
|
|
state: {
|
|
sorting,
|
|
columnFilters,
|
|
columnVisibility,
|
|
},
|
|
})
|
|
|
|
return (
|
|
<div>
|
|
<div className="flex items-center py-4">
|
|
<Input
|
|
placeholder="Filter emails..."
|
|
value={table.getColumn("email")?.getFilterValue() as string}
|
|
onChange={(event) =>
|
|
table.getColumn("email")?.setFilterValue(event.target.value)
|
|
}
|
|
className="max-w-sm"
|
|
/>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="outline" className="ml-auto">
|
|
Columns
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
{table
|
|
.getAllColumns()
|
|
.filter(
|
|
(column) => column.getCanHide()
|
|
)
|
|
.map((column) => {
|
|
return (
|
|
<DropdownMenuCheckboxItem
|
|
key={column.id}
|
|
className="capitalize"
|
|
checked={column.getIsVisible()}
|
|
onCheckedChange={(value) =>
|
|
column.toggleVisibility(!!value)
|
|
}
|
|
>
|
|
{column.id}
|
|
</DropdownMenuCheckboxItem>
|
|
)
|
|
})}
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
<div className="rounded-md border">
|
|
<Table>{ ... }</Table>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
This adds a dropdown menu that you can use to toggle column visibility.
|
|
|
|
</Steps>
|
|
|
|
## Row Selection
|
|
|
|
Next, we're going to add row selection to our table.
|
|
|
|
<Steps>
|
|
|
|
### Update column definitions
|
|
|
|
```tsx showLineNumbers title="app/payments/columns.tsx" {6,9-27}
|
|
"use client"
|
|
|
|
import { ColumnDef } from "@tanstack/react-table"
|
|
|
|
import { Badge } from "@/components/ui/badge"
|
|
import { Checkbox } from "@/components/ui/checkbox"
|
|
|
|
export const columns: ColumnDef<Payment>[] = [
|
|
{
|
|
id: "select",
|
|
header: ({ table }) => (
|
|
<Checkbox
|
|
checked={
|
|
table.getIsAllPageRowsSelected() ||
|
|
(table.getIsSomePageRowsSelected() && "indeterminate")
|
|
}
|
|
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
|
aria-label="Select all"
|
|
/>
|
|
),
|
|
cell: ({ row }) => (
|
|
<Checkbox
|
|
checked={row.getIsSelected()}
|
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
|
aria-label="Select row"
|
|
/>
|
|
),
|
|
enableSorting: false,
|
|
enableHiding: false,
|
|
},
|
|
]
|
|
```
|
|
|
|
### Update `<DataTable>`
|
|
|
|
```tsx showLineNumbers title="app/payments/data-table.tsx" {11,23,28}
|
|
export function DataTable<TData, TValue>({
|
|
columns,
|
|
data,
|
|
}: DataTableProps<TData, TValue>) {
|
|
const [sorting, setSorting] = React.useState<SortingState>([])
|
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
[]
|
|
)
|
|
const [columnVisibility, setColumnVisibility] =
|
|
React.useState<VisibilityState>({})
|
|
const [rowSelection, setRowSelection] = React.useState({})
|
|
|
|
const table = useReactTable({
|
|
data,
|
|
columns,
|
|
onSortingChange: setSorting,
|
|
onColumnFiltersChange: setColumnFilters,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getPaginationRowModel: getPaginationRowModel(),
|
|
getSortedRowModel: getSortedRowModel(),
|
|
getFilteredRowModel: getFilteredRowModel(),
|
|
onColumnVisibilityChange: setColumnVisibility,
|
|
onRowSelectionChange: setRowSelection,
|
|
state: {
|
|
sorting,
|
|
columnFilters,
|
|
columnVisibility,
|
|
rowSelection,
|
|
},
|
|
})
|
|
|
|
return (
|
|
<div>
|
|
<div className="rounded-md border">
|
|
<Table />
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
This adds a checkbox to each row and a checkbox in the header to select all rows.
|
|
|
|
### Show selected rows
|
|
|
|
You can show the number of selected rows using the `table.getFilteredSelectedRowModel()` API.
|
|
|
|
```tsx
|
|
<div className="flex-1 text-sm text-muted-foreground">
|
|
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
|
{table.getFilteredRowModel().rows.length} row(s) selected.
|
|
</div>
|
|
```
|
|
|
|
</Steps>
|
|
|
|
## Reusable Components
|
|
|
|
Here are some components you can use to build your data tables. This is from the [Tasks](/examples/tasks) demo.
|
|
|
|
### Column header
|
|
|
|
Make any column header sortable and hideable.
|
|
|
|
<ComponentSource src="/app/(app)/examples/tasks/components/data-table-column-header.tsx" />
|
|
|
|
```tsx {5}
|
|
export const columns = [
|
|
{
|
|
accessorKey: "email",
|
|
header: ({ column }) => (
|
|
<DataTableColumnHeader column={column} title="Email" />
|
|
),
|
|
},
|
|
]
|
|
```
|
|
|
|
### Pagination
|
|
|
|
Add pagination controls to your table including page size and selection count.
|
|
|
|
<ComponentSource src="/app/(app)/examples/tasks/components/data-table-pagination.tsx" />
|
|
|
|
```tsx
|
|
<DataTablePagination table={table} />
|
|
```
|
|
|
|
### Column toggle
|
|
|
|
A component to toggle column visibility.
|
|
|
|
<ComponentSource src="/app/(app)/examples/tasks/components/data-table-view-options.tsx" />
|
|
|
|
```tsx
|
|
<DataTableViewOptions table={table} />
|
|
```
|