Page links with previous and next controls for moving through a long list.

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

export default function PaginationDemo() {
  return (
    <Pagination>
      <PaginationContent>
        <PaginationItem>
          <PaginationPrevious href="#" />
        </PaginationItem>
        <PaginationItem>
          <PaginationLink href="#">1</PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationLink href="#" isActive>
            2
          </PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationLink href="#">3</PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationEllipsis />
        </PaginationItem>
        <PaginationItem>
          <PaginationLink href="#">10</PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationNext href="#" />
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  )
}

Installation

pnpm dlx shadcn@latest add https://kagadmodyaa.vercel.app/r/pagination.json

Needs a React + Tailwind project set up with shadcn. Copies the component source straight into your project, so you can edit it.

Pagination imports cn() from lib/utils.ts — add it too if you don't have it yet:

import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Its links are styled with buttonVariants. The CLI installs Button for you; if you copy manually, add components/ui/button.tsx too.

Usage

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

<Pagination>
  <PaginationContent>
    <PaginationItem>
      <PaginationPrevious href="?page=1" />
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="?page=1">1</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="?page=2" isActive>2</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationEllipsis />
    </PaginationItem>
    <PaginationItem>
      <PaginationNext href="?page=3" />
    </PaginationItem>
  </PaginationContent>
</Pagination>

Examples

Interactive

'use client'

import * as React from 'react'
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationPrevious,
  PaginationNext,
  PaginationEllipsis,
} from '@/components/ui/pagination'

const TOTAL = 12

/** The pages to show: the first, the last, and a window around the current one — with gaps as null. */
function visiblePages(current: number, total: number): (number | null)[] {
  const pages = new Set([1, total, current - 1, current, current + 1])
  const sorted = [...pages].filter(p => p >= 1 && p <= total).sort((a, b) => a - b)
  const out: (number | null)[] = []
  sorted.forEach((page, i) => {
    if (i > 0 && page - sorted[i - 1] > 1) out.push(null)
    out.push(page)
  })
  return out
}

export default function PaginationInteractive() {
  const [page, setPage] = React.useState(6)
  const go = (next: number) => (event: React.MouseEvent) => {
    event.preventDefault()
    setPage(Math.min(TOTAL, Math.max(1, next)))
  }

  return (
    <Pagination>
      <PaginationContent>
        <PaginationItem>
          <PaginationPrevious href="#" onClick={go(page - 1)} aria-disabled={page === 1} className={page === 1 ? 'pointer-events-none opacity-50' : undefined} />
        </PaginationItem>
        {visiblePages(page, TOTAL).map((p, i) =>
          p === null ? (
            <PaginationItem key={`gap-${i}`}>
              <PaginationEllipsis />
            </PaginationItem>
          ) : (
            <PaginationItem key={p}>
              <PaginationLink href="#" isActive={p === page} onClick={go(p)}>
                {p}
              </PaginationLink>
            </PaginationItem>
          ),
        )}
        <PaginationItem>
          <PaginationNext href="#" onClick={go(page + 1)} aria-disabled={page === TOTAL} className={page === TOTAL ? 'pointer-events-none opacity-50' : undefined} />
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  )
}

API Reference

NameTypeDefaultDescription
Pagination<nav>—The navigation landmark, labelled "pagination".
PaginationLink isActivebooleanfalseMarks the current page: outlined, and announced with aria-current="page".
PaginationLink size'sm' | 'default' | 'lg' | 'icon''icon'Button size of the link.
PaginationPrevious / PaginationNext<a>—Links with a chevron and a label that hides on small screens. Pass children to replace the label.
PaginationEllipsis<span>—A gap in the page numbers. Decorative, hidden from assistive technology.