Pagination components

FREE

Free and open-source React pagination components built for modern applications and websites. These pagination components are built using React Aria and styled with Tailwind CSS.

Installation

You can add this pagination component using our CLI or manually:

npx untitledui@latest add pagination

Pagination examples

Below are examples and variations of this pagination component:

API reference

Import the parts and assemble them:

import { Pagination } from "@/components/application/pagination/pagination-base";
 
<Pagination.Root page={page} total={total}>
    <Pagination.PrevTrigger />
    <Pagination.Context>
        {({ pages }) => (
            <>
                <Pagination.Item />
                <Pagination.Ellipsis />
            </>
        )}
    </Pagination.Context>
    <Pagination.NextTrigger />
</Pagination.Root>

Pagination.Context

Pagination.Context props
PropTypeDefaultDescription
children* (required)(pagination: PaginationContextType) => ReactNode—A render prop called with the pagination state (pages, current page, total and the page change handler).

Pagination.Ellipsis

Renders a <span> element.

Pagination.Ellipsis props
PropTypeDefaultDescription
childrenReactNode—The content rendered in place of the skipped pages, such as an ellipsis character. Hidden from assistive technology.
styleCSSProperties—The style of the ellipsis.
classNamestring | (() => string)—The class name of the ellipsis. Can be a function returning the class name.

Pagination.Item

Renders a <button> element.

Pagination.Item props
PropTypeDefaultDescription
value* (required)number—The value of the pagination item.
isCurrent* (required)boolean—Whether the pagination item is the current page.
childrenReactNode | ((props: PaginationItemRenderProps) => ReactNode)—The children of the pagination item. Can be a render prop or a valid element.
styleCSSProperties—The style object of the pagination item.
classNamestring | ((args: { isSelected: boolean; }) => string)—The class name of the pagination item.
ariaLabelstring—The aria label of the pagination item.
asChildbooleanfalseIf true, the child element will be cloned and passed down the prop of the item.

Pagination.Item

Import Pagination from @/components/application/pagination/pagination and use it as Pagination.Item.

Pagination.Item props
PropTypeDefaultDescription
value* (required)number—The page number this item navigates to, also used as its label.
isCurrent* (required)boolean—Whether this item is the page currently being viewed, which highlights it and marks it as the current page.
roundedboolean—Whether the button is a circle rather than a rounded square.

Pagination.NextTrigger

Pagination.NextTrigger props
PropTypeDefaultDescription
children* (required)ReactNode | ((props: TriggerRenderProps) => ReactNode)—The children of the trigger. Can be a render prop or a valid element.
styleCSSProperties—The style of the trigger.
ariaLabelstring—The aria label of the trigger.
classNamestring | ((args: { isDisabled: boolean; }) => string)—The class name of the trigger.
asChildboolean—If true, the child element will be cloned and passed down the prop of the trigger.

Pagination.PrevTrigger

Pagination.PrevTrigger props
PropTypeDefaultDescription
children* (required)ReactNode | ((props: TriggerRenderProps) => ReactNode)—The children of the trigger. Can be a render prop or a valid element.
styleCSSProperties—The style of the trigger.
ariaLabelstring—The aria label of the trigger.
classNamestring | ((args: { isDisabled: boolean; }) => string)—The class name of the trigger.
asChildboolean—If true, the child element will be cloned and passed down the prop of the trigger.

Pagination.Root

Renders a <nav> element.

Pagination.Root props
PropTypeDefaultDescription
page* (required)number—Current active page number.
total* (required)number—Total number of pages.
children* (required)ReactNode—The pagination parts to render, such as Pagination.PrevTrigger, Pagination.Context and Pagination.NextTrigger.
siblingCountnumber1Number of sibling pages to show on each side of the current page.
styleCSSProperties—The style of the pagination root.
classNamestring—The class name of the pagination root.
onPageChange((page: number) => void)—Callback function that's called when the page changes with the new page number.

PaginationButtonGroup

Import PaginationButtonGroup from @/components/application/pagination/pagination. Renders a <div> element.

PaginationButtonGroup props
PropTypeDefaultDescription
align
"left""center""right"
"left"The alignment of the pagination.
totalnumber10Total number of pages.
styleCSSProperties—The style of the pagination root.
pagenumber1Current active page number.
classNamestring—The class name of the pagination root.
siblingCountnumber—Number of sibling pages to show on each side of the current page.
onPageChange((page: number) => void)—Callback function that's called when the page changes with the new page number.

PaginationCardAdvanced

Import PaginationCardAdvanced from @/components/application/pagination/pagination. Renders a <div> element.

PaginationCardAdvanced props
PropTypeDefaultDescription
pagenumber1The current page.
totalnumber10The total number of pages.
pageSizenumber10The number of items per page.
align
"space-between""center"
"space-between"The alignment of the pagination.
classNamestring—The class name of the pagination component.
onPageChange((page: number) => void)—The function to call when the page changes.
onPageSizeChange((pageSize: number) => void)—The function to call when the page size changes.

PaginationCardDefault

Import PaginationCardDefault from @/components/application/pagination/pagination. Renders a <div> element.

PaginationCardDefault props
PropTypeDefaultDescription
roundedboolean—Whether the pagination buttons are rounded.
totalnumber10Total number of pages.
styleCSSProperties—The style of the pagination root.
pagenumber1Current active page number.
classNamestring"flex flex-1 justify-start"The class name of the pagination root.
siblingCountnumber—Number of sibling pages to show on each side of the current page.
onPageChange((page: number) => void)—Callback function that's called when the page changes with the new page number.

PaginationCardMinimal

Import PaginationCardMinimal from @/components/application/pagination/pagination. Renders a <div> element.

PaginationCardMinimal props
PropTypeDefaultDescription
pagenumber1The current page.
totalnumber10The total number of pages.
pageSizenumber10The number of items per page.
align
"left""center""right"
"left"The alignment of the pagination.
classNamestring—The class name of the pagination component.
onPageChange((page: number) => void)—The function to call when the page changes.
onPageSizeChange((pageSize: number) => void)—The function to call when the page size changes.

PaginationDot

Import PaginationDot from @/components/application/pagination/pagination-dot.

PaginationDot props
PropTypeDefaultDescription
total* (required)number—Total number of pages.
page* (required)number—Current active page number.
size
"md""lg"
"md"The size of the pagination dot.
isBrandboolean—Whether the pagination uses brand colors.
framedboolean—Whether the pagination is displayed in a card.
styleCSSProperties—The style of the pagination root.
classNamestring—The class name of the pagination root.
siblingCountnumber—Number of sibling pages to show on each side of the current page.
onPageChange((page: number) => void)—Callback function that's called when the page changes with the new page number.

PaginationLine

Import PaginationLine from @/components/application/pagination/pagination-line.

PaginationLine props
PropTypeDefaultDescription
total* (required)number—Total number of pages.
page* (required)number—Current active page number.
size
"md""lg"
"md"The size of the pagination line.
framedboolean—Whether the pagination is displayed in a card.
styleCSSProperties—The style of the pagination root.
classNamestring—The class name of the pagination root.
siblingCountnumber—Number of sibling pages to show on each side of the current page.
onPageChange((page: number) => void)—Callback function that's called when the page changes with the new page number.

PaginationPageDefault

Import PaginationPageDefault from @/components/application/pagination/pagination. Renders a <div> element.

PaginationPageDefault props
PropTypeDefaultDescription
roundedboolean—Whether the pagination buttons are rounded.
totalnumber10Total number of pages.
styleCSSProperties—The style of the pagination root.
pagenumber1Current active page number.
classNamestring"hidden flex-1 justify-start md:flex"The class name of the pagination root.
siblingCountnumber—Number of sibling pages to show on each side of the current page.
onPageChange((page: number) => void)—Callback function that's called when the page changes with the new page number.

PaginationPageMinimalCenter

Import PaginationPageMinimalCenter from @/components/application/pagination/pagination. Renders a <div> element.

PaginationPageMinimalCenter props
PropTypeDefaultDescription
roundedboolean—Whether the pagination buttons are rounded.
totalnumber10Total number of pages.
styleCSSProperties—The style of the pagination root.
pagenumber1Current active page number.
classNamestring"flex flex-1 justify-start"The class name of the pagination root.
siblingCountnumber—Number of sibling pages to show on each side of the current page.
onPageChange((page: number) => void)—Callback function that's called when the page changes with the new page number.

FAQs

Please refer to our frequently asked questions page for more.

pagination.tsx exports PaginationPageDefault, PaginationPageMinimalCenter, PaginationCardDefault, PaginationCardMinimal, PaginationButtonGroup and PaginationCardAdvanced. Two carousel-style indicators live in their own files: PaginationDot and PaginationLine, both of which take a size of "md" or "lg" (default "md") and a framed flag for the translucent pill background.

It is fully controlled. You pass the current page as page (1-based) and the number of pages as total, and the component calls onPageChange(page: number) with the new page number. It never stores the page internally, so nothing moves until you update your own state. On Pagination.Root, PaginationDot and PaginationLine both props are required and have no default; the flat variants such as PaginationPageDefault, PaginationCardMinimal and PaginationCardAdvanced fall back to page = 1 and total = 10 when you omit them.

Because page and onPageChange are the whole API, read the current page from wherever it lives — a URL search param, for example — and push a new URL inside onPageChange instead of calling setState. Compute total as the page count, not the row count: Math.ceil(totalItems / pageSize).

Use siblingCount, which defaults to 1 and sets how many pages appear on each side of the current page. The component renders every page without ellipsis while siblingCount * 2 + 5 is greater than or equal to total, and otherwise collapses the run with Pagination.Ellipsis while always keeping the first and last page visible. PaginationCardMinimal and PaginationCardAdvanced don't accept siblingCount.

Yes — PaginationCardMinimal and PaginationCardAdvanced accept pageSize (default 10) and onPageSizeChange, and render a Select with 10, 25, 50 and 100 options. PaginationCardAdvanced additionally renders an editable page number input and first/last page jump buttons.

PaginationCardMinimal and PaginationButtonGroup accept align with "left", "center" or "right" (default "left"), while PaginationCardAdvanced accepts "space-between" or "center" (default "space-between"). The rounded prop, which switches the number buttons from rounded-lg to rounded-full, is accepted by three variants only: PaginationPageDefault, PaginationPageMinimalCenter and PaginationCardDefault. PaginationCardMinimal, PaginationCardAdvanced and PaginationButtonGroup do not take it.

Pagination.Root renders a <nav aria-label="Pagination Navigation">. Each page button gets aria-current="page" when it is the current page and an aria-label of "Page {value}", the ellipsis is aria-hidden, and the previous and next triggers are labelled "Previous Page" and "Next Page" and are automatically disabled on the first and last page. Those button labels are the ones you can override: Pagination.Item, Pagination.PrevTrigger and Pagination.NextTrigger each take an ariaLabel prop. The <nav> label is hardcoded on Pagination.Root, so change it by editing the component. PaginationCardMinimal is the exception: it doesn't use Pagination.Root and renders its own <nav aria-label="Pagination"> with plain Previous and Next buttons.