Skip to main content
Pagination helps users navigate through large sets of data by breaking content into pages. It supports both simple and numbered variants with customizable display options.

Import

import { Pagination } from '@/components/ui/Pagination';

Usage

Basic Pagination

const [currentPage, setCurrentPage] = useState(1);

<Pagination
  currentPage={currentPage}
  totalPages={10}
  onPageChange={setCurrentPage}
/>

Variants

Pagination supports two main variants:

Numbered Pagination (default)

<Pagination
  variant="numbered"
  currentPage={5}
  totalPages={20}
  onPageChange={(page) => console.log(page)}
  showPrevNext={true}
/>

Simple Pagination

<Pagination
  variant="simple"
  currentPage={5}
  totalPages={20}
  onPageChange={(page) => console.log(page)}
/>
Simple variant displays “Page X of Y” instead of individual page numbers.

Sizes

Three sizes are available:
<Pagination size="sm" currentPage={3} totalPages={10} />
<Pagination size="md" currentPage={5} totalPages={10} />
<Pagination size="lg" currentPage={7} totalPages={10} />

With Previous/Next Buttons

<Pagination
  currentPage={5}
  totalPages={20}
  showPrevNext={true}
  onPageChange={(page) => console.log(page)}
/>

With First/Last Buttons

<Pagination
  currentPage={5}
  totalPages={20}
  showPrevNext={true}
  showFirstLast={true}
  onPageChange={(page) => console.log(page)}
/>

Sibling Count

Control how many page numbers to show on each side of the current page:
// Shows 1 page on each side (default)
<Pagination
  currentPage={10}
  totalPages={50}
  siblingCount={1}
  onPageChange={(page) => console.log(page)}
/>

// Shows 2 pages on each side
<Pagination
  currentPage={10}
  totalPages={50}
  siblingCount={2}
  onPageChange={(page) => console.log(page)}
/>

Examples

Basic Data Table Pagination

import { useState } from 'react';

function DataTable() {
  const [currentPage, setCurrentPage] = useState(1);
  const itemsPerPage = 10;
  const totalItems = 237;
  const totalPages = Math.ceil(totalItems / itemsPerPage);

  return (
    <div>
      {/* Your table content */}
      
      <div className="mt-4 flex justify-center">
        <Pagination
          currentPage={currentPage}
          totalPages={totalPages}
          onPageChange={setCurrentPage}
        />
      </div>
    </div>
  );
}

With API Data Fetching

import { useState, useEffect } from 'react';

function ProductList() {
  const [currentPage, setCurrentPage] = useState(1);
  const [data, setData] = useState([]);
  const [totalPages, setTotalPages] = useState(1);

  useEffect(() => {
    async function fetchData() {
      const response = await fetch(`/api/products?page=${currentPage}`);
      const json = await response.json();
      setData(json.items);
      setTotalPages(json.totalPages);
    }
    fetchData();
  }, [currentPage]);

  return (
    <div>
      <div className="grid grid-cols-3 gap-4">
        {data.map((product) => (
          <ProductCard key={product.id} {...product} />
        ))}
      </div>
      
      <Pagination
        currentPage={currentPage}
        totalPages={totalPages}
        onPageChange={setCurrentPage}
        size="md"
        showPrevNext
        showFirstLast
      />
    </div>
  );
}

Compact Mobile Layout

<Pagination
  variant="simple"
  currentPage={5}
  totalPages={20}
  size="sm"
  onPageChange={(page) => console.log(page)}
/>
<Pagination
  variant="numbered"
  currentPage={10}
  totalPages={50}
  size="lg"
  showPrevNext={true}
  showFirstLast={true}
  siblingCount={2}
  onPageChange={(page) => console.log(page)}
/>

Props

variant
string
default:"numbered"
The display style. Options: numbered (shows page numbers), simple (shows “Page X of Y”).
currentPage
number
default:"1"
The currently active page number (1-indexed).
totalPages
number
default:"1"
The total number of pages available.
size
string
default:"md"
The size of pagination controls. Options: sm, md, lg.
onPageChange
(page: number) => void
Callback function called when the user navigates to a different page. Receives the new page number.
showPrevNext
boolean
default:"true"
Whether to show previous and next navigation buttons.
showFirstLast
boolean
default:"false"
Whether to show first and last page buttons. Only applies to numbered variant.
siblingCount
number
default:"1"
Number of page buttons to show on each side of the current page. Only applies to numbered variant.
className
string
Additional CSS classes to apply to the pagination container.

Components

The Pagination component also exports sub-components for custom implementations:

PaginationItem

Individual page button component:
import { PaginationItem } from '@/components/ui/Pagination';

<PaginationItem
  variant="default"
  size="md"
  isActive={true}
  onClick={() => console.log('clicked')}
>
  5
</PaginationItem>

PaginationItem Props

variant
string
default:"default"
Button style. Options: default, ghost, outline.
size
string
default:"md"
Button size. Options: sm, md, lg.
isActive
boolean
default:"false"
Whether this page is currently active.

PaginationEllipsis

Ellipsis indicator for hidden pages:
import { PaginationEllipsis } from '@/components/ui/Pagination';

<PaginationEllipsis size="md" />

Pagination Logic

The component includes a usePagination hook that intelligently displays page numbers:
  • Shows all pages when total pages fit in the available space
  • Adds ellipsis (...) to indicate hidden pages
  • Always shows first and last page numbers when there are many pages
  • Displays sibling pages around the current page based on siblingCount

Examples:

Few pages (all shown):
1 2 3 4 5
Current page near start:
1 2 3 4 5 ... 20
Current page in middle:
1 ... 8 9 10 11 12 ... 50
Current page near end:
1 ... 16 17 18 19 20

Semantic Tokens

Pagination uses the following semantic tokens from the Stride Design System:
  • --button-height-sm, --button-height-md, --button-height-lg
  • --spacing-xs, --spacing-sm, --spacing-md, --spacing-lg
  • --font-size-sm, --font-size-md, --font-size-lg
  • --radius-sm, --radius-md, --radius-lg
  • --text-primary, --text-secondary
  • --bg-primary, --bg-tertiary
  • --border-primary, --border-secondary, --border-focus
  • --interactive-primary, --interactive-primary-text
  • --interactive-ghost-hover
  • --transition-fast

Accessibility

  • All buttons have descriptive aria-label or sr-only text for screen readers
  • Previous/Next buttons include “Go to previous page” / “Go to next page” labels
  • Ellipsis has “More pages” label for screen readers
  • Current page button uses isActive state for visual indication
  • Focus visible outlines on all interactive elements
  • Disabled states for previous button on first page and next button on last page
  • Keyboard navigation supported (Tab to focus, Enter/Space to activate)
  • Buttons meet minimum touch target size (44x44px) on all sizes

Build docs developers (and LLMs) love