HextaUI

Pagination

Page navigation that keeps every button in place, glides to the current page and jumps to any page from the ellipsis.

Showing 1–10 of 248 results

"use client"

import * as React from "react"

import { Pagination } from "@/components/ui/pagination"

const perPage = 10
const total = 248

export function PaginationDemo() {
  const [page, setPage] = React.useState(1)
  const first = (page - 1) * perPage + 1
  const last = Math.min(page * perPage, total)

  return (
    <div className="flex w-full flex-col items-center gap-4">
      <Pagination
        page={page}
        count={Math.ceil(total / perPage)}
        onPageChange={setPage}
      />
      <p className="text-sm text-muted-foreground tabular-nums">
        Showing {first}–{last} of {total} results
      </p>
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/pagination.json

Adds the component, the HextaUI theme tokens and any HextaUI components it depends on.

import { Pagination } from "@/components/ui/pagination"
const [page, setPage] = React.useState(1)

<Pagination page={page} count={25} onPageChange={setPage} />

Give Pagination a count and it builds the whole bar. It always shows the same number of slots, so Previous, Next and the numbers never shift under your cursor while you click through pages. Leave count out to compose the parts yourself.

Pagination
└── PaginationContent
    ├── PaginationItem
    │   └── PaginationPrevious
    ├── PaginationItem
    │   └── PaginationLink
    ├── PaginationItem
    │   └── PaginationEllipsis
    └── PaginationItem
        └── PaginationNext

Jump to a page

Click an ellipsis to type a page number. Enter jumps, an out-of-range number shakes the field, and Esc puts the ellipsis back. Turn it off with jump={false}.

"use client"

import * as React from "react"

import { Pagination } from "@/components/ui/pagination"

export function PaginationLong() {
  const [page, setPage] = React.useState(480)

  return <Pagination page={page} count={1000} onPageChange={setPage} />
}

Compact

When the bar doesn't fit its container, it switches to Previous, “Page 4 of 24” and Next, and switches back when there's room. The label is a jump field too. Force either layout with compact.

"use client"

import * as React from "react"

import { Pagination } from "@/components/ui/pagination"

export function PaginationCompact() {
  const [page, setPage] = React.useState(4)

  return (
    <div className="w-full max-w-72 rounded-xl border p-2">
      <Pagination page={page} count={24} onPageChange={setPage} />
    </div>
  )
}

With getPageHref, every page is a real link, so it can be opened in a new tab. Plain clicks still call onPageChange.

"use client"

import * as React from "react"

import { Pagination } from "@/components/ui/pagination"

export function PaginationLinks() {
  const [page, setPage] = React.useState(3)

  return (
    <Pagination
      page={page}
      count={12}
      getPageHref={(target) => `#page-${target}`}
      onPageChange={setPage}
    />
  )
}

Composable

Without count, build the bar from the parts, as in shadcn/ui. Pass render to use your router's link.

"use client"

import * as React from "react"

import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from "@/components/ui/pagination"

const pages = [1, 2, 3]

export function PaginationComposable() {
  const [page, setPage] = React.useState(2)

  return (
    <Pagination>
      <PaginationContent>
        <PaginationItem>
          <PaginationPrevious
            render={<button type="button" />}
            disabled={page === 1}
            onClick={() => setPage(page - 1)}
          />
        </PaginationItem>
        {pages.map((item) => (
          <PaginationItem key={item}>
            <PaginationLink
              render={<button type="button" />}
              isActive={item === page}
              onClick={() => setPage(item)}
            >
              {item}
            </PaginationLink>
          </PaginationItem>
        ))}
        <PaginationItem>
          <PaginationEllipsis />
        </PaginationItem>
        <PaginationItem>
          <PaginationNext
            render={<button type="button" />}
            disabled={page === pages.length}
            onClick={() => setPage(page + 1)}
          />
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  )
}

Right to left

usePagination returns the page and ellipsis items for custom layouts. Chevrons flip in right-to-left text.

"use client"

import * as React from "react"

import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
  usePagination,
} from "@/components/ui/pagination"

export function PaginationRtl() {
  const [page, setPage] = React.useState(2)
  const { items, hasPrevious, hasNext } = usePagination({ page, count: 3 })

  return (
    <div dir="rtl">
      <Pagination aria-label="التنقل بين الصفحات">
        <PaginationContent>
          <PaginationItem>
            <PaginationPrevious
              text="السابق"
              aria-label="الصفحة السابقة"
              render={<button type="button" />}
              disabled={!hasPrevious}
              onClick={() => setPage(page - 1)}
            />
          </PaginationItem>
          {items.map((item) =>
            item.type === "page" ? (
              <PaginationItem key={item.page}>
                <PaginationLink
                  render={<button type="button" />}
                  isActive={item.page === page}
                  onClick={() => setPage(item.page)}
                >
                  {item.page.toLocaleString("ar-EG")}
                </PaginationLink>
              </PaginationItem>
            ) : null
          )}
          <PaginationItem>
            <PaginationNext
              text="التالي"
              aria-label="الصفحة التالية"
              render={<button type="button" />}
              disabled={!hasNext}
              onClick={() => setPage(page + 1)}
            />
          </PaginationItem>
        </PaginationContent>
      </Pagination>
    </div>
  )
}
KeyAction
TabMoves into and out of the bar. Previous and Next stay focusable at the ends.
←→Moves between Previous, the pages and Next. Flips in right-to-left layouts.
HomeEndMoves to the first or last item.
EnterOpens the focused page or the page field. In the field, jumps to that page and focuses it.
EscCloses the page field and returns focus to the ellipsis.
  • The root is a <nav> labelled “Pagination”, and the current page has aria-current="page".
  • At the first and last page, Previous and Next use aria-disabled instead of disabled, so focus isn't lost when you reach the end.
  • The sliding indicator becomes a fade when reduced motion is on.
PropTypeDefault
countTotal pages. When set, the bar is built for you; leave it out to compose the parts.
number–
page
number–
defaultPage
number1
onPageChange
(page: number) => void–
getPageHrefRenders pages as links instead of buttons.
(page: number) => string–
siblingsPages shown on each side of the current one.
number1
boundariesPages always shown at the start and end.
number1
compactauto switches to Previous, “Page x of y” and Next when the bar doesn't fit.
boolean | "auto""auto"
jumpLets the ellipsis open a page field.
booleantrue
AttributeDescription
data-slot="pagination"The nav.
data-slot="pagination-indicator"The background that glides to the current page.
PropTypeDefault
isActiveMarks the current page with aria-current="page".
booleanfalse
disabledSets aria-disabled and ignores clicks.
booleanfalse
render
ReactElement | (props, state) => ReactElement<a>
AttributeDescription
data-slot="pagination-link"Target page links in CSS.
data-activePresent on the current page.
PropTypeDefault
textPass null for an icon-only button.
ReactNode"Previous" / "Next"
disabled
booleanfalse
PropTypeDefault
onJumpMakes the ellipsis a page field. Without it, the ellipsis is decorative.
(page: number) => void–
countThe highest page the field accepts.
number–
PropTypeDefault
page
number–
count
number–
siblings
number1
boundaries
number1

Returns items, the clamped page and count, and hasPrevious and hasNext. See the usePagination guide for building your own pager.