Component
Data Table
Data Table
Powerful table and datagrids built using TanStack Table.
Status | Amount | |||
---|---|---|---|---|
success | ken99@yahoo.com | $316.00 | ||
success | Abe45@gmail.com | $242.00 | ||
processing | Monserrat44@gmail.com | $837.00 | ||
success | Silas22@gmail.com | $874.00 | ||
failed | carmella@hotmail.com | $721.00 |
0 of 5 row(s) selected.
Installation
Install the following dependencies
npm i @tanstack/react-table
Add the Table
component to your project.
We'll use the Table
component for the Data Table. Make sure you have it installed in your project.
Guide
For the data table, we'll use TanStack Table and the <Table />
component. It's a powerful table component that allows you to build any kind of table or datagrid.
Please refer to the detailed guide from shadcn/ui for how to create your own data table.
Reusable Components
Here are some reusable components you can use to build your data tables.
Column Header
Make any column header sortable and hideable.
components/previews/data-table/column-header.tsx
import { ArrowDownIcon, ArrowUpIcon, ArrowUpDownIcon, EyeOffIcon } from 'lucide-react'
import { Column } from '@tanstack/react-table'
import { css } from '@shadow-panda/styled-system/css'
import { Flex } from '@shadow-panda/styled-system/jsx'
import { icon } from '@shadow-panda/styled-system/recipes'
import { Button } from '@/components/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
interface DataTableColumnHeaderProps<TData, TValue> extends React.HTMLAttributes<HTMLDivElement> {
column: Column<TData, TValue>
title: string
}
export function DataTableColumnHeader<TData, TValue>({
column,
title,
className,
}: DataTableColumnHeaderProps<TData, TValue>) {
if (!column.getCanSort()) {
return <div className={className}>{title}</div>
}
return (
<Flex align="center" gap="2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="sm"
className={css({
ml: '-3',
h: '8',
'&[data-state="open]': {
bg: 'accent',
},
})}
>
<span>{title}</span>
{column.getIsSorted() === 'desc' ? (
<ArrowDownIcon className={icon({ left: 'sm' })} />
) : column.getIsSorted() === 'asc' ? (
<ArrowUpIcon className={icon({ left: 'sm' })} />
) : (
<ArrowUpDownIcon className={icon({ left: 'sm' })} />
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem onClick={() => column.toggleSorting(false)}>
<ArrowUpIcon
className={css({ mr: '2', h: '3.5', w: '3.5', ca: 'muted.foreground/70' })}
/>
Asc
</DropdownMenuItem>
<DropdownMenuItem onClick={() => column.toggleSorting(true)}>
<ArrowDownIcon
className={css({ mr: '2', h: '3.5', w: '3.5', ca: 'muted.foreground/70' })}
/>
Desc
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
<EyeOffIcon
className={css({ mr: '2', h: '3.5', w: '3.5', ca: 'muted.foreground/70' })}
/>
Hide
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</Flex>
)
}
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.
components/previews/data-table/pagination.tsx
import {
ChevronLeftIcon,
ChevronRightIcon,
ChevronsLeftIcon,
ChevronsRightIcon,
} from 'lucide-react'
import { Table } from '@tanstack/react-table'
import { css } from '@shadow-panda/styled-system/css'
import { Flex, Box } from '@shadow-panda/styled-system/jsx'
import { icon } from '@shadow-panda/styled-system/recipes'
import { Button } from '@/components/ui/button'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
interface DataTablePaginationProps<TData> {
table: Table<TData>
}
export function DataTablePagination<TData>({ table }: DataTablePaginationProps<TData>) {
return (
<Flex align="center" justify="space-between" px="2">
<Box flex="1" textStyle="sm" color="muted.foreground">
{table.getFilteredSelectedRowModel().rows.length} of{' '}
{table.getFilteredRowModel().rows.length} row(s) selected.
</Box>
<Flex align="center" gap="6" lg={{ gap: '8' }}>
<Flex align="center" gap="2">
<p className={css({ textStyle: 'sm', fontWeight: 'medium' })}>Rows per page</p>
<Select
value={`${table.getState().pagination.pageSize}`}
onValueChange={(value) => {
table.setPageSize(Number(value))
}}
>
<SelectTrigger h="8" w="70px">
<SelectValue placeholder={table.getState().pagination.pageSize} />
</SelectTrigger>
<SelectContent side="top">
{[10, 20, 30, 40, 50].map((pageSize) => (
<SelectItem key={pageSize} value={`${pageSize}`}>
{pageSize}
</SelectItem>
))}
</SelectContent>
</Select>
</Flex>
<Flex w="100px" align="center" justify="center" textStyle="sm" fontWeight="medium">
Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
</Flex>
<Flex align="center" gap="2">
<Button
variant="outline"
display="none"
h="8"
w="8"
p="0"
lg={{ display: 'flex' }}
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
<span className={css({ srOnly: true })}>Go to first page</span>
<ChevronsLeftIcon className={icon()} />
</Button>
<Button
variant="outline"
h="8"
w="8"
p="0"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
<span className={css({ srOnly: true })}>Go to previous page</span>
<ChevronLeftIcon className={icon()} />
</Button>
<Button
variant="outline"
h="8"
w="8"
p="0"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
<span className={css({ srOnly: true })}>Go to next page</span>
<ChevronRightIcon className={icon()} />
</Button>
<Button
variant="outline"
display="none"
h="8"
w="8"
p="0"
lg={{ display: 'flex' }}
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
<span className={css({ srOnly: true })}>Go to last page</span>
<ChevronsRightIcon className={icon()} />
</Button>
</Flex>
</Flex>
</Flex>
)
}
<DataTablePagination table={table} />
Column Toggle
A component to toggle column visibility.
components/previews/data-table/column-toggle.tsx
'use client'
import { SlidersHorizontalIcon } from 'lucide-react'
import { Table } from '@tanstack/react-table'
import { icon } from '@shadow-panda/styled-system/recipes'
import { Button } from '@/components/ui/button'
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
interface DataTableViewOptionsProps<TData> {
table: Table<TData>
}
export function DataTableViewOptions<TData>({ table }: DataTableViewOptionsProps<TData>) {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" ml="auto" display="none" h="8" lg={{ display: 'flex' }}>
<SlidersHorizontalIcon className={icon({ right: 'sm' })} />
View
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" w="150px">
<DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
<DropdownMenuSeparator />
{table
.getAllColumns()
.filter((column) => typeof column.accessorFn !== 'undefined' && column.getCanHide())
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
textTransform="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) => column.toggleVisibility(!!value)}
>
{column.id}
</DropdownMenuCheckboxItem>
)
})}
</DropdownMenuContent>
</DropdownMenu>
)
}
<DataTableViewOptions table={table} />