Pagination
A flexible and customizable pagination component for navigating through large datasets. Built on top of rc-pagination, the Pagination component seamlessly integrates with RizzUI's design system, providing a consistent and accessible user experience for paginated content.
Features
- 📄 Flexible Navigation - Easy navigation through paginated content
- 🎨 Multiple Variants - Choose from solid or flat styles
- 🔲 Outline Mode - Optional outline style for icons and items
- ⚙️ Customizable Icons - Customize previous, next, and jump icons
- 📊 Page Size Control - Built-in support for page size selection
- 🔍 Quick Jumper - Jump to any page quickly
- ♿ Accessible - Built with accessibility best practices
- 🎯 Type Safe - Full TypeScript support
Installation
Before using the Pagination component, you'll need to install the required dependency:
Step 1
Install the rc-pagination package.
- npm
- yarn
- pnpm
- bun
npm install rc-pagination
yarn add rc-pagination
pnpm add rc-pagination
bun add rc-pagination
Step 2
Create a pagination component, components/pagination.tsx
import React from 'react';
import 'rc-pagination/assets/index.css';
import RcPagination, {
PaginationProps as RcPaginationProps,
} from 'rc-pagination';
import { tv, type VariantProps } from 'tailwind-variants';
import { cn } from 'rizzui/cn';
const pagination = tv({
slots: {
container:
'[&>.rc-pagination-item>a]:!no-underline [&>.rc-pagination-item>a]:!font-medium [&>li.rc-pagination-item]:!border-muted [&>.rc-pagination-item:not(.rc-pagination-item-active)]:!bg-transparent rounded-[var(--border-radius)]',
icon: '',
jumperDiv:
'[&>.rc-pagination-options>.rc-pagination-options-quick-jumper]:!text-sm [&>.rc-pagination-options>.rc-pagination-options-quick-jumper]:!text-gray-500',
jumperInput:
'[&>.rc-pagination-options>.rc-pagination-options-quick-jumper>input]:!py-[3px] [&>.rc-pagination-options>.rc-pagination-options-quick-jumper>input]:!text-sm [&>.rc-pagination-options>.rc-pagination-options-quick-jumper>input]:!border-muted [&>.rc-pagination-options>.rc-pagination-options-quick-jumper>input]:!ring-0 rounded-[var(--border-radius)]',
},
variants: {
variant: {
solid: {
container:
'[&>.rc-pagination-item-active]:!bg-primary [&>.rc-pagination-item-active>a]:!text-primary-foreground [&>li.rc-pagination-item-active]:!border-primary [&>.rc-pagination-item-active]:hover:!border-primary [&>.rc-pagination-item-active]:focus:!border-primary',
},
flat: {
container:
'[&>.rc-pagination-item-active]:!bg-primary-lighter [&>li.rc-pagination-item-active]:border-primary-lighter [&>.rc-pagination-item-active>a]:text-primary-dark [&>.rc-pagination-item-active>a]:hover:text-primary-dark [&>.rc-pagination-item-active>a]:focus:text-primary-dark [&>.rc-pagination-item-active]:hover:border-primary-lighter [&>.rc-pagination-item-active]:focus:!border-primary-lighter',
},
},
outline: {
true: {
container:
'[&>.rc-pagination-item]:!leading-7 [&>.rc-pagination-item]:!border-0',
icon: '[&>.rc-pagination-prev]:!align-baseline [&>.rc-pagination-next]:!align-baseline',
},
false: {
container:
'[&>.rc-pagination-item]:!leading-7 [&>.rc-pagination-item]:!border-0',
icon: '[&>.rc-pagination-prev]:!align-baseline [&>.rc-pagination-next]:!align-baseline',
},
},
},
defaultVariants: {
variant: 'solid',
outline: false,
},
});
const iconTV = tv({
base: 'text-foreground rounded-[var(--border-radius)]',
variants: {
outline: {
true: 'border border-muted p-[5px]',
false: 'inline-block align-middle',
},
},
defaultVariants: {
outline: false,
},
});
type IconProps = {
icon: React.ReactNode;
outline: boolean;
className: string;
};
const PrevIcon = ({ icon, outline, className }: IconProps) => (
<div className={iconTV({ outline, className })}>
{icon || (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={2}
stroke="currentColor"
className="m-auto h-4 w-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15.75 19.5L8.25 12l7.5-7.5"
/>
</svg>
)}
</div>
);
const NextIcon = ({ icon, outline, className }: IconProps) => (
<div className={iconTV({ outline, className })}>
{icon || (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={2}
stroke="currentColor"
className="m-auto h-4 w-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M8.25 4.5l7.5 7.5-7.5 7.5"
/>
</svg>
)}
</div>
);
const JumpPrevIcon = ({ icon, outline, className }: IconProps) => (
<div
className={iconTV({
outline,
className: cn(!icon && outline && 'py-0 leading-[26px]', className),
})}
>
{icon || '•••'}
</div>
);
const JumpNextIcon = ({ icon, outline, className }: IconProps) => (
<div
className={iconTV({
outline,
className: cn(!icon && outline && 'py-0 leading-[26px]', className),
})}
>
{icon || '•••'}
</div>
);
export const localeDefault = {
items_per_page: '/ page',
jump_to: 'Go to',
jump_to_confirm: 'confirm',
page: 'Page',
prev_page: 'Previous Page',
next_page: 'Next Page',
prev_5: 'Previous 5 Pages',
next_5: 'Next 5 Pages',
prev_3: 'Previous 3 Pages',
next_3: 'Next 3 Pages',
page_size: 'Page Size',
};
export interface PaginationProps extends RcPaginationProps {
outline?: boolean;
variant?: VariantProps<typeof pagination>['variant'];
prevIconClassName?: string;
nextIconClassName?: string;
jumpPrevIconClassName?: string;
jumpNextIconClassName?: string;
}
export default function Pagination({
outline = false,
variant = 'solid',
locale,
nextIcon,
prevIcon,
prevIconClassName,
nextIconClassName,
jumpPrevIcon,
jumpNextIcon,
jumpPrevIconClassName,
jumpNextIconClassName,
className,
...props
}: PaginationProps) {
const {
container: containerClass,
icon: iconClass,
jumperDiv: jumperDivClass,
jumperInput: jumperInputClass,
} = pagination({
variant,
outline,
});
return (
<RcPagination
locale={locale || localeDefault}
nextIcon={
<NextIcon
icon={nextIcon as React.ReactNode}
outline={outline}
className={nextIconClassName as string}
/>
}
prevIcon={
<PrevIcon
icon={prevIcon as React.ReactNode}
outline={outline}
className={prevIconClassName as string}
/>
}
jumpPrevIcon={
<JumpPrevIcon
icon={jumpPrevIcon as React.ReactNode}
outline={outline}
className={jumpPrevIconClassName as string}
/>
}
jumpNextIcon={
<JumpNextIcon
icon={jumpNextIcon as React.ReactNode}
outline={outline}
className={jumpNextIconClassName as string}
/>
}
className={cn(
containerClass(),
iconClass(),
jumperDivClass(),
jumperInputClass(),
className
)}
{...props}
/>
);
}
Pagination.displayName = 'Pagination';
Usage
Basic Example
The simplest way to use the Pagination component with default settings:
import Pagination from '@components/pagination';
export default function App() {
return <Pagination total={25} defaultCurrent={1} />;
}
Controlled Component
For controlled usage, manage the current page with state:
import React from 'react';
import Pagination from '@components/pagination';
export default function App() {
const [current, setCurrent] = React.useState(1);
return (
<Pagination
total={100}
current={current}
onChange={(page) => setCurrent(page)}
/>
);
}
Variants
The Pagination component supports two visual variants to match your design needs:
import Pagination from '@components/pagination';
export default function App() {
return (
<>
<Pagination total={25} variant="solid" defaultCurrent={1} />
<Pagination total={25} variant="flat" defaultCurrent={1} />
</>
);
}
Outline Mode
Enable outline mode to add borders around pagination items and icons:
import Pagination from '@components/pagination';
export default function App() {
return <Pagination total={25} outline={true} defaultCurrent={1} />;
}
Disabled State
Disable the pagination component to prevent user interaction:
import Pagination from '@components/pagination';
export default function App() {
return <Pagination total={25} defaultCurrent={1} disabled />;
}
Custom Icons
Customize the previous and next icons with text or React components:
import Pagination from '@components/pagination';
export default function App() {
return (
<Pagination
total={100}
defaultCurrent={5}
nextIcon="Next"
prevIcon="Previous"
prevIconClassName="py-0 text-foreground !leading-[26px]"
nextIconClassName="py-0 text-foreground !leading-[26px]"
/>
);
}
Custom Jump Icons
Customize the jump previous and jump next icons for better visual feedback:
import Pagination from '@components/pagination';
import {
ChevronDoubleRightIcon,
ChevronDoubleLeftIcon,
} from '@heroicons/react/24/outline';
export default function App() {
return (
<Pagination
outline
total={100}
defaultCurrent={5}
jumpPrevIcon={<ChevronDoubleLeftIcon className="h-4 w-4" />}
jumpNextIcon={<ChevronDoubleRightIcon className="h-4 w-4" />}
/>
);
}
Advanced Usage
Page Size Selection
Enable page size selection to let users choose how many items to display per page:
import Pagination from '@components/pagination';
export default function App() {
return (
<Pagination
total={100}
defaultCurrent={1}
showSizeChanger
pageSizeOptions={['10', '20', '50', '100']}
onShowSizeChange={(current, size) => {
console.log(`Page: ${current}, Size: ${size}`);
}}
/>
);
}
Quick Jumper
Enable quick jumper to allow users to jump directly to a specific page:
import Pagination from '@components/pagination';
export default function App() {
return (
<Pagination
total={100}
defaultCurrent={1}
showQuickJumper
onChange={(page) => {
console.log(`Jumped to page: ${page}`);
}}
/>
);
}
Show Total
Display the total number of items and current range:
import Pagination from '@components/pagination';
export default function App() {
return (
<Pagination
total={100}
defaultCurrent={1}
showTotal={(total, range) => `${range[0]}-${range[1]} of ${total} items`}
/>
);
}
Simple Mode
Use simple mode for compact pagination:
import Pagination from '@components/pagination';
export default function App() {
return <Pagination total={100} defaultCurrent={1} simple />;
}
API Reference
Pagination Props
| Prop | Type | Description | Default |
|---|---|---|---|
| variant | "solid" | "flat" | Visual style variant of the component | "solid" |
| outline | boolean | Whether to show outline style for items and icons | false |
| disabled | boolean | Disable all pagination interactions | false |
| total | number | Total number of data items | undefined |
| current | number | Current page number (controlled) | undefined |
| defaultCurrent | number | Default page number (uncontrolled) | undefined |
| pageSize | number | Number of data items per page (controlled) | undefined |
| defaultPageSize | number | Default number of data items per page | undefined |
| showSizeChanger | boolean | Show page size selector | false |
| pageSizeOptions | string[] | number[] | Specify the size options | undefined |
| showQuickJumper | boolean | object | Show quick jumper to jump to any page | false |
| showTotal | (total: number, range: [number, number]) => ReactNode | Show total number and range | undefined |
| simple | boolean | Simple mode for compact pagination | false |
| hideOnSinglePage | boolean | Hide pagination when there's only one page | false |
| prevIcon | ReactNode | ComponentType | Custom previous icon | undefined |
| nextIcon | ReactNode | ComponentType | Custom next icon | undefined |
| jumpPrevIcon | ReactNode | ComponentType | Custom jump previous icon | undefined |
| jumpNextIcon | ReactNode | ComponentType | Custom jump next icon | undefined |
| prevIconClassName | string | Additional CSS classes for previous icon | undefined |
| nextIconClassName | string | Additional CSS classes for next icon | undefined |
| jumpPrevIconClassName | string | Additional CSS classes for jump previous icon | undefined |
| jumpNextIconClassName | string | Additional CSS classes for jump next icon | undefined |
| onChange | (page: number, pageSize: number) => void | Callback when page changes | undefined |
| onShowSizeChange | (current: number, size: number) => void | Callback when page size changes | undefined |
| className | string | Additional CSS classes for the root element | undefined |
Note: The Pagination component extends all props from rc-pagination, allowing you to use features like
itemRender,locale,showLessItems, and more. Refer to the rc-pagination documentation for a complete list of available props.
Pagination Variants
type PaginationVariants = 'solid' | 'flat';
- solid - Active page with solid background color (default)
- flat - Active page with lighter background color
Best Practices
- Use appropriate total - Always provide an accurate total count for better UX
- Handle page changes - Use
onChangecallback to update your data source - Consider page size - Enable
showSizeChangerfor large datasets - Provide quick jumper - Enable
showQuickJumperfor datasets with many pages - Show totals - Use
showTotalto give users context about the dataset size - Accessibility - The component includes built-in ARIA attributes for screen readers
- Mobile considerations - Consider using
simplemode on mobile devices