Light

Pagination

Pagination with page navigation, previous and next links.

Spec · from metadata

When to use

  • Navigating between pages of content (tables, lists, search results)
  • Server-side or client-side paginated data

When not to use

  • Infinite scroll patterns — use intersection observer instead
  • Step-by-step wizard — use Stepper or Tabs
  • Few items that fit on one page — no pagination needed

Anti-patterns

Avoid<Pagination>
  <PaginationPrevious />
  <PaginationLink isActive>1</PaginationLink>
  <PaginationNext />
</Pagination>
Prefer<Pagination>
  <PaginationContent>
    <PaginationItem><PaginationPrevious href="#" /></PaginationItem>
    <PaginationItem><PaginationLink href="#" isActive>1</PaginationLink></PaginationItem>
    <PaginationItem><PaginationNext href="#" /></PaginationItem>
  </PaginationContent>
</Pagination>

Wrap in PaginationContent and PaginationItem for proper list semantics (nav > ul > li > a).

Accessibility

  • Required ARIAaria-label='pagination' (set automatically)
  • Min touch target36px
  • Screen readerRenders as <nav role='navigation' aria-label='pagination'>. Active page has aria-current='page'. Ellipsis includes sr-only 'More pages' text.

Token bindings

TokenCategoryUsage
backgroundcolorDefault link background
accentcolorLink hover state
inputcolorActive page outline border
rounded-mdradiusLink border radius

Import

import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationPrevious,
  PaginationNext,
  PaginationEllipsis,
} from "@timelycare/helix-ui"

Props

interface PaginationLinkProps {
  isActive?: boolean
  className?: string
  href?: string
}

interface PaginationItemProps {
  variant: "previous" | "next" | "link" | "ellipsis"
  state?: "default" | "hover" | "focus" | "active" | "disabled"
}

Item Variants

VariantUse ForTailwind
previousNavigate to previous pagegap-1 h-9 pl-2.5 pr-4 rounded-md
nextNavigate to next pagegap-1 h-9 pl-4 pr-2.5 rounded-md
linkPage number buttonssize-9 rounded-md
ellipsisIndicate skipped pagessize-9 p-2.5

Choosing a Variant

  • Use previous/next for sequential navigation at edges
  • Use link for direct page access
  • Use ellipsis between visible page ranges (e.g., 1 2 ... 8 9)

States

StateVariantTailwind
Defaultprevious/nextbg-transparent text-foreground
Defaultlinkbg-transparent text-foreground
Hoverprevious/nextbg-accent text-accent-foreground
Hoverlinkbg-accent text-accent-foreground
Focusallring-2 ring-ring ring-offset-2
Activelinkbg-background border border-input shadow-xs
Active+Hoverlinkbg-primary text-primary-foreground
Disabledalltext-muted-foreground pointer-events-none opacity-50

Icons

Size: h-4 w-4 (16px) Spacing: gap-1 between icon and text

<PaginationPrevious href="#" />  {/* ChevronLeft + "Previous" */}
<PaginationNext href="#" />      {/* "Next" + ChevronRight */}
<PaginationEllipsis />           {/* MoreHorizontal icon */}

Common Patterns

Basic Pagination

<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>
      <PaginationNext href="#" />
    </PaginationItem>
  </PaginationContent>
</Pagination>

Disabled Navigation (First Page)

<PaginationPrevious 
  href="#" 
  aria-disabled="true"
  className="pointer-events-none opacity-50 text-muted-foreground"
/>

Controlled State

<PaginationLink 
  onClick={(e) => { e.preventDefault(); setPage(n); }}
  isActive={currentPage === n}
>
  {n}
</PaginationLink>

Accessibility

  • Rendered as <nav> with aria-label="pagination"
  • Keyboard: Tab between page links, Enter to navigate
  • Screen reader: current page announced via aria-current="page"
  • Disable Previous/Next buttons at bounds with aria-disabled

Gotchas

ProblemSolution
Active page not styledAdd isActive prop to PaginationLink
No focus ring visibleEnsure ring-offset-background is set on parent
Nav buttons always activeAdd disabled styling when on first/last page

See Also


Last updated: February 9, 2026