Skip to main content

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 install 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

PropTypeDescriptionDefault
variant"solid" | "flat"Visual style variant of the component"solid"
outlinebooleanWhether to show outline style for items and iconsfalse
disabledbooleanDisable all pagination interactionsfalse
totalnumberTotal number of data itemsundefined
currentnumberCurrent page number (controlled)undefined
defaultCurrentnumberDefault page number (uncontrolled)undefined
pageSizenumberNumber of data items per page (controlled)undefined
defaultPageSizenumberDefault number of data items per pageundefined
showSizeChangerbooleanShow page size selectorfalse
pageSizeOptionsstring[] | number[]Specify the size optionsundefined
showQuickJumperboolean | objectShow quick jumper to jump to any pagefalse
showTotal(total: number, range: [number, number]) => ReactNodeShow total number and rangeundefined
simplebooleanSimple mode for compact paginationfalse
hideOnSinglePagebooleanHide pagination when there's only one pagefalse
prevIconReactNode | ComponentTypeCustom previous iconundefined
nextIconReactNode | ComponentTypeCustom next iconundefined
jumpPrevIconReactNode | ComponentTypeCustom jump previous iconundefined
jumpNextIconReactNode | ComponentTypeCustom jump next iconundefined
prevIconClassNamestringAdditional CSS classes for previous iconundefined
nextIconClassNamestringAdditional CSS classes for next iconundefined
jumpPrevIconClassNamestringAdditional CSS classes for jump previous iconundefined
jumpNextIconClassNamestringAdditional CSS classes for jump next iconundefined
onChange(page: number, pageSize: number) => voidCallback when page changesundefined
onShowSizeChange(current: number, size: number) => voidCallback when page size changesundefined
classNamestringAdditional CSS classes for the root elementundefined

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 onChange callback to update your data source
  • Consider page size - Enable showSizeChanger for large datasets
  • Provide quick jumper - Enable showQuickJumper for datasets with many pages
  • Show totals - Use showTotal to give users context about the dataset size
  • Accessibility - The component includes built-in ARIA attributes for screen readers
  • Mobile considerations - Consider using simple mode on mobile devices