Skip to Content
ComponentsData Table

Data Table

A powerful table with sorting, filtering, and pagination built on TanStack Table .

Status
Amount
successken99@example.com
$316.00
successabe45@example.com
$242.00
processingmonserrat44@example.com
$837.00
successsilas22@example.com
$874.00
failedcarmella@example.com
$721.00
pendingalex@example.com
$125.00
successdiana@example.com
$540.00

The Data Table is a composition pattern. Create your column definitions, then pass data and columns to useReactTable.

1. Define your columns:

// columns.tsx "use client" import { type ColumnDef } from "@tanstack/react-table" import { Button } from "@/components/ui/button" import { ArrowUpDown } from "lucide-react" 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: ({ column }) => ( <Button variant="ghost" size="sm" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")} > Email <ArrowUpDown className="ml-2 size-4" /> </Button> ), }, { 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> }, }, ]

2. Build the table component:

// data-table.tsx "use client" import * as React from "react" import { type ColumnDef, type ColumnFiltersState, type SortingState, flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, } from "@tanstack/react-table" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" 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 [sorting, setSorting] = React.useState<SortingState>([]) const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]) const table = useReactTable({ data, columns, onSortingChange: setSorting, onColumnFiltersChange: setColumnFilters, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(), 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={(e) => table.getColumn("email")?.setFilterValue(e.target.value)} className="max-w-sm" /> </div> <div className="rounded-md border"> <Table> <TableHeader> {table.getHeaderGroups().map((hg) => ( <TableRow key={hg.id}> {hg.headers.map((header) => ( <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> <div className="flex items-center justify-end gap-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> ) }

3. Render it:

import { columns } from "./columns" import { DataTable } from "./data-table" export default function Page() { const data = await fetchPayments() // your data source return <DataTable columns={columns} data={data} /> }
Last updated on