Preview
Installation
CLI
npx shadcn@latest add https://kelvinmai.io/r/pagination.json
Manual
Install the following dependencies
npm install clsx tailwind-merge
Add a classname utility function
import { clsx, type ClassValue } from 'clsx';import { twMerge } from 'tailwind-merge';export const cn = (...inputs: ClassValue[]) => {return twMerge(clsx(inputs));};
Copy and paste the following code into your project
components/pagination.tsx
import * as React from 'react';import {ChevronLeft,ChevronRight,Ellipsis,EllipsisVertical,} from 'lucide-react';import { Button } from '@/components/ui/button';import {DropdownMenu,DropdownMenuContent,DropdownMenuLabel,DropdownMenuRadioItem,DropdownMenuSeparator,DropdownMenuTrigger,} from '@/components/ui/dropdown-menu';import { cn } from '@/lib/utils';import { DropdownMenuRadioGroup } from '@radix-ui/react-dropdown-menu';type PaginationProps = React.ComponentProps<'nav'> & {currentPage: number;pages: number;onPage: (page: number) => void;buttonProps?: Omit<React.ComponentProps<typeof Button>,'disabled' | 'onClick'>;};export const Pagination: React.FC<PaginationProps> = ({className,currentPage,pages,onPage,buttonProps,children,...props}) => {return (<navrole='navigation'aria-label='pagination'className={cn('mx-auto flex w-full justify-center gap-1 my-4', className)}{...props}><ButtononClick={() => onPage(currentPage - 1)}disabled={currentPage <= 1}{...buttonProps}><ChevronLeft /><span>Previous</span></Button>{currentPage >= 3 && (<Button disabled {...buttonProps}><Ellipsis /></Button>)}{currentPage >= 2 && (<Button onClick={() => onPage(currentPage - 1)} {...buttonProps}>{currentPage - 1}</Button>)}<Button disabled {...buttonProps}>{currentPage}</Button>{currentPage < pages - 1 && (<Button onClick={() => onPage(currentPage + 1)} {...buttonProps}>{currentPage + 1}</Button>)}{pages - currentPage >= 2 && (<Button disabled {...buttonProps}><Ellipsis /></Button>)}{currentPage !== pages && (<Button onClick={() => onPage(pages)} {...buttonProps}>{pages}</Button>)}<ButtononClick={() => onPage(currentPage + 1)}disabled={currentPage >= pages}{...buttonProps}><span>Next</span><ChevronRight /></Button>{children}</nav>);};type PaginationPageSizeDropdownProps = React.ComponentProps<typeof DropdownMenu> & {buttonProps?: React.ComponentProps<typeof Button>;pageSizes?: number[];total: number;pageSize: number;onPageSize: (pageSize: number) => void;};export const PaginationPageSizeDropdown: React.FC<PaginationPageSizeDropdownProps> = ({buttonProps,pageSizes = [5, 10, 25],total,pageSize,onPageSize,...props}) => {return (<DropdownMenu {...props}><DropdownMenuTrigger asChild><Button {...buttonProps}><EllipsisVertical /></Button></DropdownMenuTrigger><DropdownMenuContent><DropdownMenuLabel>Total: {total}</DropdownMenuLabel><DropdownMenuSeparator /><DropdownMenuLabel>Select Page Size</DropdownMenuLabel><DropdownMenuSeparator /><DropdownMenuRadioGroupvalue={String(pageSize)}onValueChange={(v) => onPageSize(Number(v))}>{pageSizes.map((n) => (<DropdownMenuRadioItem key={n} value={String(n)}>{n}</DropdownMenuRadioItem>))}</DropdownMenuRadioGroup></DropdownMenuContent></DropdownMenu>);};
Update the import paths to match your project setup
Usage
import { Pagination } from '@/components/ui/pagination';export default function PaginationDemo() {const [pagination, setPagination] = React.useState({currentPage: 1,pages: 10,});const [limit, setLimit] = React.useState(10);const buttonProps = { variant: 'ghost' };return (<PaginationcurrentPage={pagination.currentPage}pages={pagination.pages}onPage={(page) => setPagination({ ...pagination, currentPage: page })}buttonProps={buttonProps}><PaginationPageSizeDropDowntotal={1000}pageSize={limit}onPageSize={(pageSize) => setLimit(pageSize)}buttonProps={buttonProps}/></Pagination>);}
API Reference
Pagination props
Name | Type | Required | Description |
---|---|---|---|
currentPage | number | Yes | The value of the current page. |
pages | number | Yes | The value of total pages of the data set. |
onPage | (page: number) => void | Yes | Function to call when page changes. |
buttonProps | React.ComponentProps<typeof Button> | No | Props for each of the pagination buttons. |
PaginationPageSizeDropDown props
Name | Type | Required | Description |
---|---|---|---|
pageSize | number | Yes | The value of how many items to display per page |
total | number | Yes | The total value of how many items are in the data set. |
pageSizes | number[] | No | The page size options. The default options are [5, 10, 25] |
onPageSize | (pageSize: number) => void | Yes | Function to update the item size of each page. |
buttonProps | React.ComponentProps<typeof Button> | No | Props for each of the pagination buttons. |