Skip to content
EMBOSS
Docs menu

Pagination

Page navigation where the current page is a latched, pressed-in key with tabular numerals.

@emboss/pagination

Installation

pnpm dlx shadcn@latest add @emboss/pagination

Usage

example.tsx
import {
  Pagination,
  PaginationContent,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from "@/components/ui/pagination";

export function Example() {
  return (
    <Pagination>
      <PaginationContent>
        <PaginationPrevious href="#previous" />
        <PaginationLink href="#1" isActive>
          1
        </PaginationLink>
        <PaginationLink href="#2">2</PaginationLink>
        <PaginationNext href="#next" />
      </PaginationContent>
    </Pagination>
  );
}

API reference

Pagination / PaginationContent / PaginationLink / PaginationPrevious / PaginationNext / PaginationEllipsis

A labelled nav of page links rendered as anchors — wire href (or compose with your router's Link via the className).

PropTypeDefaultDescription
isActivebooleanfalseMarks the current page: latched depth plus aria-current (PaginationLink).

Source

View source — pagination.tsx
pagination.tsx
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
import { cn } from "@/lib/utils";

/**
 * Page navigation where the current page is a latched, pressed-in key.
 *
 * @example
 * <Pagination>
 *   <PaginationContent>
 *     <PaginationPrevious href="#" />
 *     <PaginationLink href="#" isActive>1</PaginationLink>
 *     <PaginationLink href="#">2</PaginationLink>
 *     <PaginationNext href="#" />
 *   </PaginationContent>
 * </Pagination>
 */
function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
  return (
    <nav
      data-slot="pagination"
      aria-label="Pagination"
      className={cn("mx-auto flex w-full justify-center", className)}
      {...props}
    />
  );
}

function PaginationContent({
  className,
  ...props
}: React.ComponentProps<"ul">) {
  return (
    <ul
      data-slot="pagination-content"
      className={cn("flex items-center gap-1.5", className)}
      {...props}
    />
  );
}

const linkClassName =
  "focus-ring inline-flex h-(--control-sm) min-w-(--control-sm) cursor-pointer items-center justify-center gap-1 rounded-sm px-2 text-sm font-medium transition-[box-shadow,background-color,color] duration-(--duration-press) ease-(--ease-press) select-none";

function PaginationLink({
  className,
  isActive = false,
  children,
  ...props
}: React.ComponentProps<"a"> & {
  /** Marks the current page: latched into the surface with aria-current. */
  isActive?: boolean;
}) {
  return (
    <li data-slot="pagination-item">
      <a
        data-slot="pagination-link"
        aria-current={isActive ? "page" : undefined}
        className={cn(
          linkClassName,
          isActive
            ? "bg-well font-mono text-ink tabular-nums shadow-deboss-1"
            : "font-mono text-ink-muted tabular-nums hover:bg-surface-2 hover:text-ink hover:shadow-emboss-1",
          className,
        )}
        {...props}
      >
        {children}
      </a>
    </li>
  );
}

function PaginationPrevious({
  className,
  ...props
}: React.ComponentProps<"a">) {
  return (
    <li data-slot="pagination-item">
      <a
        data-slot="pagination-previous"
        className={cn(
          linkClassName,
          "px-2.5 text-ink-muted hover:bg-surface-2 hover:text-ink hover:shadow-emboss-1",
          className,
        )}
        {...props}
      >
        <ChevronLeft className="size-4" aria-hidden />
        <span className="hidden sm:inline">Previous</span>
      </a>
    </li>
  );
}

function PaginationNext({ className, ...props }: React.ComponentProps<"a">) {
  return (
    <li data-slot="pagination-item">
      <a
        data-slot="pagination-next"
        className={cn(
          linkClassName,
          "px-2.5 text-ink-muted hover:bg-surface-2 hover:text-ink hover:shadow-emboss-1",
          className,
        )}
        {...props}
      >
        <span className="hidden sm:inline">Next</span>
        <ChevronRight className="size-4" aria-hidden />
      </a>
    </li>
  );
}

function PaginationEllipsis({
  className,
  ...props
}: React.ComponentProps<"span">) {
  return (
    <li data-slot="pagination-item">
      <span
        data-slot="pagination-ellipsis"
        aria-hidden
        className={cn(
          "flex size-(--control-sm) items-center justify-center text-ink-faint",
          className,
        )}
        {...props}
      >
        <MoreHorizontal className="size-4" />
        <span className="sr-only">More pages</span>
      </span>
    </li>
  );
}

export {
  Pagination,
  PaginationContent,
  PaginationLink,
  PaginationPrevious,
  PaginationNext,
  PaginationEllipsis,
};