www.rizzui.com
Open in
urlscan Pro
76.76.21.164
Public Scan
Submitted URL: https://rizzui.com/docs/integrations/tan-stack
Effective URL: https://www.rizzui.com/docs/integrations/tan-stack
Submission: On November 04 via api from US — Scanned from US
Effective URL: https://www.rizzui.com/docs/integrations/tan-stack
Submission: On November 04 via api from US — Scanned from US
Form analysis
0 forms found in the DOMText Content
Skip to main content DocumentationComponents ctrlK * Guide * Getting Started * Dark Mode * Theming * Buttons * ActionIcon * Button * Inputs * Input * Password * Textarea * Switch * Checkbox * AdvancedCheckbox * Checkbox Group * Radio * AdvancedRadio * Radio Group * Select * MultiSelect * PinCode * NumberInput * FileInput * Navigation * Dropdown * Tabs * Stepper * Feedback * Alert * Loader * Progressbar * Radial Progressbar * Overlays * Tooltip * Popover * Modal * Drawer * Data Display * Accordion * Avatar * Announcement * Badge * Empty * Table * Typography * Title * Text * Code * Blockquote * Layouts * Grid * Flex * Box * Integrations * Rc Rate * Rc Slider * Rc Table * TanStack Table * Rc Pagination * PhoneNumber * React Datepicker * React Hook Form * * Integrations * TanStack Table On this page TANSTACK TABLE HeadlessUI components for building highly interactive and accessible tables. In these example we have used our own Table components with TanStack table. Status... CustomerDue DateAmountStatus Francis Sanford MD marya.barrow@yahoo.com October 18, 20233:24 AM $ 544 Paid Lucia Kshlerin mason_davis4@yahoo.com July 17, 20233:06 PM $ 560 Pending Byron Hoppe III jayda_schill35@yahoo.com December 18, 20245:32 AM $ 249 Pending Camille Jenkins retha.lehne47@hotmail.com June 30, 20249:06 AM $ 255 Draft Kelli Mitchell guise.champ@hotmail.com July 24, 20258:45 AM $ 329 Paid 0 of 15 row(s) selected. Rows per page 5 Page 1 of 3 INSTALLATION -------------------------------------------------------------------------------- 1. Install the @tanstack/react-table package. npm install @tanstack/react-table 2. Create a table component table.tsx import React from "react"; import { Table } from "rizzui"; import { type Person } from "./data"; import { flexRender, Table as TanStackTableTypes } from "@tanstack/react-table"; type TablePropsTypes = { table: TanStackTableTypes<Person>; }; export default function MainTable({ table }: TablePropsTypes) { const footers = table .getFooterGroups() .map((group) => group.headers.map((header) => header.column.columnDef.footer)) .flat() .filter(Boolean); return ( <div className="w-full overflow-x-auto overflow-y-hidden custom-scrollbar"> <Table className="!shadow-none !border-0" style={{ width: table.getTotalSize(), }} > <Table.Header className="!bg-gray-100 !border-y-0"> {table.getHeaderGroups().map((headerGroup) => { return ( <Table.Row key={headerGroup.id}> {headerGroup.headers.map((header) => { return ( <Table.Head key={header.id} colSpan={header.colSpan} style={{ width: header.getSize(), }} className="!text-start" > {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} </Table.Head> ); })} </Table.Row> ); })} </Table.Header> <Table.Body> {table.getRowModel().rows.map((row) => ( <Table.Row key={row.id}> {row.getVisibleCells().map((cell) => ( <Table.Cell key={cell.id} className="!text-start" style={{ width: cell.column.getSize(), }} > {flexRender(cell.column.columnDef.cell, cell.getContext())} </Table.Cell> ))} </Table.Row> ))} </Table.Body> {footers.length > 0 && ( <Table.Footer> {table.getFooterGroups().map((footerGroup) => ( <Table.Row key={footerGroup.id}> {footerGroup.headers.map((footer) => { return ( <Table.Cell key={footer.id}> {footer.isPlaceholder ? null : ( <>{flexRender(footer.column.columnDef.footer, footer.getContext())}</> )} </Table.Cell> ); })} </Table.Row> ))} </Table.Footer> )} </Table> </div> ); } 3. Create a column.tsx file import { type Person } from "./data"; import { ActionIcon, Badge, Button, Checkbox, Popover, Text } from "rizzui"; import { createColumnHelper } from "@tanstack/react-table"; import { AvatarCard, DateCell, getStatusBadge } from "./utils"; import { EllipsisHorizontalIcon, EyeIcon, PencilIcon, TrashIcon, } from "@heroicons/react/24/outline"; const columnHelper = createColumnHelper<Person>(); export const defaultColumns = [ columnHelper.accessor("id", { size: 50, header: ({ table }) => ( <Checkbox className="ps-2" inputClassName="bg-white" aria-label="Select all rows" checked={table.getIsAllPageRowsSelected()} onChange={() => table.toggleAllPageRowsSelected()} /> ), cell: ({ row }) => ( <Checkbox className="ps-2" aria-label="Select row" checked={row.getIsSelected()} onChange={() => row.toggleSelected()} /> ), }), columnHelper.accessor("name", { size: 280, header: "Customer", cell: ({ row: { original } }) => ( <AvatarCard src={original.avatar} name={original.name} description={original.email.toLowerCase()} /> ), }), columnHelper.accessor("dueDate", { size: 180, header: "Due Date", cell: ({ row }) => <DateCell date={new Date(row.original.dueDate)} />, }), columnHelper.accessor("amount", { size: 120, header: "Amount", cell: ({ row }) => <span className="font-medium">$ {row.original.amount}</span>, }), columnHelper.accessor("status", { size: 120, header: "Status", cell: (info) => getStatusBadge(info.renderValue()!), }), columnHelper.accessor("avatar", { size: 120, header: "", cell: () => ( <div className="w-full flex justify-center"> <Popover shadow="sm" placement="bottom-end" > <Popover.Trigger> <ActionIcon variant="text"> <EllipsisHorizontalIcon strokeWidth={2} className="size-5" /> </ActionIcon> </Popover.Trigger> <Popover.Content className="max-w-40 grid grid-cols-1 gap-1 p-1"> <Button variant="text" className="hover:bg-gray-100 gap-2" > <PencilIcon className="size-4" /> Edit </Button> <Button variant="text" className="hover:bg-gray-100 gap-2" > <EyeIcon className="size-4" /> View </Button> <Button variant="text" color="danger" className="hover:bg-gray-100 gap-2" > <TrashIcon className="size-4" /> Delete </Button> </Popover.Content> </Popover> </div> ), }), ]; 4. Create a data.ts file export type Person = { id: string; name: string; userName: string; avatar: string; email: string; dueDate: string; amount: number; status: string; }; export const defaultData = [ { id: "62447", name: "Francis Sanford MD", userName: "George33", avatar: "https://randomuser.me/api/portraits/women/8.jpg", email: "Marya.Barrow@yahoo.com", dueDate: "2023-10-18T13:24:00.760Z", amount: 544, status: "Paid", }, { id: "86740", name: "Lucia Kshlerin", userName: "Kenyon_Goldner56", avatar: "https://randomuser.me/api/portraits/women/2.jpg", email: "Mason_Davis4@yahoo.com", dueDate: "2023-07-18T01:06:16.095Z", amount: 560, status: "Pending", }, { id: "42548", name: "Byron Hoppe III", userName: "Walton.Hane98", avatar: "https://randomuser.me/api/portraits/men/75.jpg", email: "Jayda_Schill35@yahoo.com", dueDate: "2024-12-18T15:32:21.317Z", amount: 249, status: "Pending", }, { id: "97024", name: "Camille Jenkins", userName: "Dalton_Von55", avatar: "https://randomuser.me/api/portraits/men/9.jpg", email: "Retha.Lehne47@hotmail.com", dueDate: "2024-06-30T19:06:03.018Z", amount: 255, status: "Draft", }, { id: "14608", name: "Kelli Mitchell", userName: "Iva.Denesik", avatar: "https://randomuser.me/api/portraits/men/22.jpg", email: "Guise.Champ@hotmail.com", dueDate: "2025-07-24T18:45:02.179Z", amount: 329, status: "Paid", }, { id: "95656", name: "Randall Kuhic", userName: "Henry_Quigley0", avatar: "https://randomuser.me/api/portraits/men/1.jpg", email: "Simeon93@yahoo.com", dueDate: "2023-11-02T00:20:47.253Z", amount: 402, status: "Paid", }, { id: "73151", name: "Jody Carroll", userName: "Lavon32", avatar: "https://randomuser.me/api/portraits/women/8.jpg", email: "Frieda_Renne@gmail.com", dueDate: "2024-01-03T02:53:29.596Z", amount: 977, status: "Overdue", }, { id: "57931", name: "Jill Russel", userName: "Abdiel.Terry", avatar: "https://randomuser.me/api/portraits/women/2.jpg", email: "Cleora.Murra@hotmail.com", dueDate: "2025-01-23T08:52:39.081Z", amount: 736, status: "Paid", }, { id: "36515", name: "Genevieve Hammes", userName: "Kian_Huels", avatar: "https://randomuser.me/api/portraits/men/75.jpg", email: "Bernard63@yahoo.com", dueDate: "2024-07-29T18:18:19.193Z", amount: 755, status: "Draft", }, { id: "34893", name: "Alejandro Reichert", userName: "Timothy91", avatar: "https://randomuser.me/api/portraits/men/9.jpg", email: "Wava.Mulle47@gmail.com", dueDate: "2023-05-04T04:33:47.908Z", amount: 240, status: "Draft", }, { id: "66356", name: "Ricardo Kling", userName: "Celia.Shanahan86", avatar: "https://randomuser.me/api/portraits/men/22.jpg", email: "Gene73@yahoo.com", dueDate: "2025-04-16T11:49:15.276Z", amount: 852, status: "Overdue", }, { id: "81467", name: "Carl Bode", userName: "Pablo_Thompson", avatar: "https://randomuser.me/api/portraits/men/1.jpg", email: "Virgil.Skile@hotmail.com", dueDate: "2024-05-28T04:44:49.629Z", amount: 295, status: "Draft", }, { id: "14042", name: "Sherry Weber", userName: "Shane39", avatar: "https://randomuser.me/api/portraits/women/8.jpg", email: "Aidan22@hotmail.com", dueDate: "2025-11-30T00:34:34.822Z", amount: 318, status: "Paid", }, { id: "49825", name: "Erika O'Reilly", userName: "Hazle_Bednar95", avatar: "https://randomuser.me/api/portraits/women/2.jpg", email: "Ardith57@yahoo.com", dueDate: "2024-05-17T06:24:33.253Z", amount: 463, status: "Overdue", }, { id: "81929", name: "Lillian Anderson", userName: "Cyrus_Hettinger", avatar: "https://randomuser.me/api/portraits/men/75.jpg", email: "Aletha_Watrs87@gmail.com", dueDate: "2023-12-29T04:41:54.007Z", amount: 196, status: "Pending", }, { id: "25086", name: "Connie Braun", userName: "Ramona99", avatar: "https://randomuser.me/api/portraits/men/9.jpg", email: "Mervin.Ruerford@hotmail.com", dueDate: "2024-12-27T21:39:17.142Z", amount: 384, status: "Draft", }, { id: "79737", name: "Mattie Miller", userName: "Madison_MacGyver-Lesch52", avatar: "https://randomuser.me/api/portraits/men/22.jpg", email: "Bianka30@yahoo.com", dueDate: "2025-06-27T15:53:33.802Z", amount: 812, status: "Paid", }, { id: "66747", name: "Shelley Lind-VonRueden", userName: "Issac_West", avatar: "https://randomuser.me/api/portraits/men/1.jpg", email: "Destini_Wiamson34@yahoo.com", dueDate: "2024-12-29T09:04:48.858Z", amount: 596, status: "Pending", }, { id: "44937", name: "Manuel Langworth", userName: "Kelley71", avatar: "https://randomuser.me/api/portraits/men/75.jpg", email: "Philip.OKfe94@gmail.com", dueDate: "2025-12-20T09:41:31.402Z", amount: 545, status: "Pending", }, { id: "11420", name: "Dr. Guillermo Huels Jr.", userName: "Linnie.Hane", avatar: "https://randomuser.me/api/portraits/women/8.jpg", email: "Ricky41@yahoo.com", dueDate: "2023-03-26T14:06:10.093Z", amount: 537, status: "Paid", }, ]; 5. Create a pagination.tsx file import { ActionIcon, Select, SelectOption, Text } from "rizzui"; import { type Table as ReactTableType } from "@tanstack/react-table"; import { ChevronDoubleLeftIcon, ChevronDoubleRightIcon, ChevronLeftIcon, ChevronRightIcon, } from "@heroicons/react/20/solid"; const options = [ { value: 5, label: "5" }, { value: 10, label: "10" }, { value: 15, label: "15" }, { value: 20, label: "20" }, ]; export default function TablePagination<TData extends Record<string, any>>({ table, }: { table: ReactTableType<TData>; }) { return ( <div className="flex w-full items-center justify-between @container"> <div className="hidden @2xl:block"> <Text> {table.getFilteredSelectedRowModel().rows.length} of{" "} {table.getFilteredRowModel().rows.length} row(s) selected. </Text> </div> <div className="flex w-full items-center justify-between gap-6 @2xl:w-auto @2xl:gap-12"> <div className="flex items-center gap-4"> <Text className="hidden font-medium text-gray-900 @md:block">Rows per page</Text> <Select options={options} className="w-[70px]" value={table.getState().pagination.pageSize} onChange={(v: SelectOption) => { table.setPageSize(Number(v.value)); }} selectClassName="font-semibold text-sm ring-0 shadow-sm h-9" optionClassName="justify-center font-medium" /> </div> <Text className="hidden font-medium text-gray-900 @3xl:block"> Page {table.getState().pagination.pageIndex + 1} of{" "} {table.getPageCount().toLocaleString()} </Text> <div className="grid grid-cols-4 gap-2"> <ActionIcon rounded="lg" variant="outline" aria-label="Go to first page" onClick={() => table.firstPage()} disabled={!table.getCanPreviousPage()} className="text-gray-900 shadow-sm disabled:text-gray-400 disabled:shadow-none" > <ChevronDoubleLeftIcon className="size-5" /> </ActionIcon> <ActionIcon rounded="lg" variant="outline" aria-label="Go to previous page" onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()} className="text-gray-900 shadow-sm disabled:text-gray-400 disabled:shadow-none" > <ChevronLeftIcon className="size-5" /> </ActionIcon> <ActionIcon rounded="lg" variant="outline" aria-label="Go to next page" onClick={() => table.nextPage()} disabled={!table.getCanNextPage()} className="text-gray-900 shadow-sm disabled:text-gray-400 disabled:shadow-none" > <ChevronRightIcon className="size-5" /> </ActionIcon> <ActionIcon rounded="lg" variant="outline" aria-label="Go to last page" onClick={() => table.lastPage()} disabled={!table.getCanNextPage()} className="text-gray-900 shadow-sm disabled:text-gray-400 disabled:shadow-none" > <ChevronDoubleRightIcon className="size-5" /> </ActionIcon> </div> </div> </div> ); } 6. Create a toolbar.tsx file import { ActionIcon, Badge, Button, Checkbox, Input, Popover, Select, Text, Title } from "rizzui"; import { type Table as ReactTableType } from "@tanstack/react-table"; import { AdjustmentsHorizontalIcon, MagnifyingGlassIcon, TrashIcon, } from "@heroicons/react/20/solid"; interface TableToolbarProps<T extends Record<string, any>> { table: ReactTableType<T>; } const statusOptions = [ { label: "Paid", value: "Paid" }, { label: "Pending", value: "Pending" }, { label: "Draft", value: "Draft" }, ]; export default function TableToolbar<TData extends Record<string, any>>({ table, }: TableToolbarProps<TData>) { const isFiltered = table.getState().globalFilter || table.getState().columnFilters.length > 0; return ( <div className="flex items-center justify-between w-full mb-4"> <Input type="search" placeholder="Search by anything..." value={table.getState().globalFilter ?? ""} onClear={() => table.setGlobalFilter("")} onChange={(e) => table.setGlobalFilter(e.target.value)} inputClassName="h-9" clearable={true} prefix={<MagnifyingGlassIcon className="size-4" />} /> <div className="flex items-center gap-4"> <Select options={statusOptions} value={table.getColumn("status")?.getFilterValue() ?? []} onChange={(e) => table.getColumn("status")?.setFilterValue(e)} getOptionValue={(option: { value: any }) => option.value} getOptionDisplayValue={(option: { value: string }) => renderOptionDisplayValue(option.value) } placeholder="Status..." displayValue={(selected: string) => renderOptionDisplayValue(selected)} className={"w-32"} dropdownClassName="!z-20 h-auto" selectClassName="h-[38px] ring-0" /> {isFiltered && ( <Button onClick={() => { table.resetGlobalFilter(); table.resetColumnFilters(); }} variant="flat" className="gap-2" > <TrashIcon className="size-4" /> Clear </Button> )} {table && ( <Popover shadow="sm" placement="bottom-end" > <Popover.Trigger> <ActionIcon title={"Toggle Columns"}> <AdjustmentsHorizontalIcon className="size-[18px]" /> </ActionIcon> </Popover.Trigger> <Popover.Content className="z-0"> <> <Title as="h6" className="!mb-4 text-sm font-semibold" > Toggle Columns </Title> <div className="grid grid-cols-1 gap-4"> {table.getAllLeafColumns().map((column) => { return ( typeof column.columnDef.header === "string" && column.columnDef.header.length > 0 && ( <Checkbox size="sm" key={column.id} label={<>{column.columnDef.header}</>} checked={column.getIsVisible()} onChange={column.getToggleVisibilityHandler()} iconClassName="size-4 translate-x-0.5" /> ) ); })} </div> </> </Popover.Content> </Popover> )} </div> </div> ); } export function renderOptionDisplayValue(value: string) { switch (value.toLowerCase()) { case "pending": return ( <div className="flex items-center"> <Badge color="warning" renderAsDot /> <Text className="ms-2 font-medium capitalize text-orange-dark">{value}</Text> </div> ); case "paid": return ( <div className="flex items-center"> <Badge color="success" renderAsDot /> <Text className="ms-2 font-medium capitalize text-green-dark">{value}</Text> </div> ); case "overdue": return ( <div className="flex items-center"> <Badge color="danger" renderAsDot /> <Text className="ms-2 font-medium capitalize text-red-dark">{value}</Text> </div> ); default: return ( <div className="flex items-center"> <Badge renderAsDot className="bg-gray-400" /> <Text className="ms-2 font-medium capitalize text-gray-600">{value}</Text> </div> ); } } 7. Create a utils.tsx file * Note: This file is not required. If you don't need the insider components, you can remove this file. And all the components used in other file. import dayjs from "dayjs"; import { Avatar, AvatarProps, Badge, cn, Text } from "rizzui"; interface AvatarCardProps { src: string; name: string; className?: string; description?: string; avatarProps?: AvatarProps; } export function AvatarCard({ src, name, className, description, avatarProps }: AvatarCardProps) { return ( <figure className={cn("flex items-center gap-3", className)}> <Avatar name={name} src={src} {...avatarProps} /> <figcaption className="grid gap-0.5"> <Text className="font-lexend text-sm font-medium text-gray-900 dark:text-gray-700"> {name} </Text> {description && <Text className="text-[13px] text-gray-500">{description}</Text>} </figcaption> </figure> ); } interface DateCellProps { date: Date; className?: string; dateFormat?: string; dateClassName?: string; timeFormat?: string; timeClassName?: string; } export function DateCell({ date, className, timeClassName, dateClassName, dateFormat = "MMMM D, YYYY", timeFormat = "h:mm A", }: DateCellProps) { return ( <div className={cn("grid gap-1", className)}> <time dateTime={formatDate(date, "YYYY-MM-DD")} className={cn("font-medium text-gray-700", dateClassName)} > {formatDate(date, dateFormat)} </time> <time dateTime={formatDate(date, "HH:mm:ss")} className={cn("text-[13px] text-gray-500", timeClassName)} > {formatDate(date, timeFormat)} </time> </div> ); } export function formatDate(date?: Date, format: string = "DD MMM, YYYY"): string { if (!date) return ""; return dayjs(date).format(format); } export function getStatusBadge(status: string) { switch (status?.toLowerCase()) { case "pending": return ( <div className="flex items-center"> <Badge color="warning" renderAsDot /> <Text className="ms-2 font-medium text-orange-dark">{status}</Text> </div> ); case "paid": return ( <div className="flex items-center"> <Badge color="success" renderAsDot /> <Text className="ms-2 font-medium text-green-dark">{status}</Text> </div> ); case "overdue": return ( <div className="flex items-center"> <Badge color="danger" renderAsDot /> <Text className="ms-2 font-medium text-red-dark">{status}</Text> </div> ); default: return ( <div className="flex items-center"> <Badge renderAsDot className="bg-gray-400" /> <Text className="ms-2 font-medium text-gray-600">{status}</Text> </div> ); } } 8. In your page file use the MainTable component. import MainTable from "./table"; import { getCoreRowModel, getFilteredRowModel, getPaginationRowModel, useReactTable, } from "@tanstack/react-table"; import { defaultData } from "./data"; import { defaultColumns } from "./column"; import TableToolbar from "./toolbar"; import TablePagination from "./pagination"; export default function TanStackTableDemo() { const table = useReactTable({ data: defaultData, columns: defaultColumns, initialState: { pagination: { pageIndex: 0, pageSize: 5, }, }, getCoreRowModel: getCoreRowModel(), getFilteredRowModel: getFilteredRowModel(), getPaginationRowModel: getPaginationRowModel(), }); return ( <> <TableToolbar table={table} /> <MainTable table={table} /> <TablePagination table={table} /> </> ); } API REFERENCE -------------------------------------------------------------------------------- * Note: For more information, please refer to TanStack Table Documentation. Edit this page Previous Rc Table Next Rc Pagination * Installation * API Reference