DesignPagination
Pagination control with page numbers, ellipsis for large ranges, icon-only Previous/Next buttons, and optional info text.
Component
Import
tsx
import { DesignPagination } from "@/components/ui/design-pagination";
Required Props
currentPage, totalPages, onPageChange
Sizes
default, small
Features
ellipsis, info text, icon nav buttons
Auto-hide
Hidden when totalPages <= 1
Props
PropTypeDefaultDescription
currentPage*number-Current page number (1-indexed)
totalPages*number-Total number of pages
onPageChange*(page: number) => void-Callback when page changes
previousTextstring"Previous"Tooltip for Previous button (i18n)
nextTextstring"Next"Tooltip for Next button (i18n)
showInfobooleantrueShow info text (e.g., '1-50 of 500')
startIndexnumber-Start index for info text (auto-calculated if not provided)
endIndexnumber-End index for info text (auto-calculated if not provided)
totalItemsnumber-Total items count for info text
infoTextstring-Custom info text format with {start}, {end}, {total} placeholders
size"default" | "small""default"Size variant for compact layouts
classNamestring-Additional className for container
Common Patterns

Basic Pagination

Simple pagination with state
const [currentPage, setCurrentPage] = useState(1);
const PAGE_SIZE = 50;
const totalPages = Math.ceil(items.length / PAGE_SIZE);

<DesignPagination
  currentPage={currentPage}
  totalPages={totalPages}
  onPageChange={setCurrentPage}
/>

With Item Info

Shows '1-50 of 500' info text
<DesignPagination
  currentPage={currentPage}
  totalPages={totalPages}
  onPageChange={setCurrentPage}
  startIndex={(currentPage - 1) * PAGE_SIZE + 1}
  endIndex={Math.min(currentPage * PAGE_SIZE, totalItems)}
  totalItems={totalItems}
/>

Internationalized

With translated text using next-intl
const t = useTranslations("common");

<DesignPagination
  currentPage={currentPage}
  totalPages={totalPages}
  onPageChange={setCurrentPage}
  previousText={t("pagination.previous")}
  nextText={t("pagination.next")}
  infoText={t("pagination.showing", { start: "{start}", end: "{end}", total: "{total}" })}
/>

Compact Size

Smaller pagination for dense layouts
<DesignPagination
  currentPage={currentPage}
  totalPages={totalPages}
  onPageChange={setCurrentPage}
  size="small"
/>

Without Info Text

Just page numbers and navigation
<DesignPagination
  currentPage={currentPage}
  totalPages={totalPages}
  onPageChange={setCurrentPage}
  showInfo={false}
/>
When to Use
1

Data table with many rows

Use with showInfo=true and calculated indices

const PAGE_SIZE = 50;
const totalPages = Math.ceil(data.length / PAGE_SIZE);
const paginatedData = data.slice((currentPage - 1) * PAGE_SIZE, currentPage * PAGE_SIZE);

<DesignPagination
  currentPage={currentPage}
  totalPages={totalPages}
  onPageChange={(page) => {
    setCurrentPage(page);
    // Optionally scroll to top of table
  }}
  startIndex={(currentPage - 1) * PAGE_SIZE + 1}
  endIndex={Math.min(currentPage * PAGE_SIZE, data.length)}
  totalItems={data.length}
/>
2

Search results with filters

Reset page to 1 when filters change

// Reset pagination when filters change
useEffect(() => {
  setCurrentPage(1);
}, [searchQuery, filters]);

<DesignPagination
  currentPage={currentPage}
  totalPages={totalPages}
  onPageChange={setCurrentPage}
/>
3

Card grid layout

Place pagination below cards with spacing

<div className="grid grid-cols-3 gap-4">
  {paginatedItems.map(item => <Card key={item.id} {...item} />)}
</div>

<div className="mt-6 pt-4 border-t">
  <DesignPagination
    currentPage={currentPage}
    totalPages={totalPages}
    onPageChange={setCurrentPage}
  />
</div>
4

Mobile-responsive list

Component is responsive by default (stacks on mobile)

{/* Info text and controls stack on mobile, side-by-side on desktop */}
<DesignPagination
  currentPage={currentPage}
  totalPages={totalPages}
  onPageChange={setCurrentPage}
/>
Do

Always reset to page 1 when filters/search change

useEffect(() => {
  setCurrentPage(1);
}, [searchQuery, sortField, filters]);

Provide accurate startIndex/endIndex for info text

<DesignPagination
  startIndex={(currentPage - 1) * PAGE_SIZE + 1}
  endIndex={Math.min(currentPage * PAGE_SIZE, totalItems)}
  totalItems={totalItems}
/>

Use i18n props for internationalization

<DesignPagination
  previousText={t("pagination.previous")}
  nextText={t("pagination.next")}
/>
Don't

Don't show pagination when there's only one page (auto-hidden)

// Component automatically returns null when totalPages <= 1
// No need to conditionally render
// Nav buttons are disabled (not hidden) at boundaries to prevent layout shift

Don't forget to slice data when using pagination

// Wrong - showing all data
{data.map(item => ...)}

// Correct - slice data based on currentPage
const paginatedData = data.slice(
  (currentPage - 1) * PAGE_SIZE,
  currentPage * PAGE_SIZE
);
{paginatedData.map(item => ...)}

Visual Examples

Interactive examples showing pagination behavior

Default (Page 1 of 10)

First page - Previous button disabled

1-50 of 500
...

Middle Page (Page 5 of 10)

Both Previous and Next buttons enabled, ellipsis shown

201-250 of 500
......

Last Page

Next button disabled

451-500 of 500
...

Few Pages (No Ellipsis)

When totalPages <= 7, all pages shown

Compact Size

Smaller variant for dense layouts

21-30 of 100
...

Interactive Demo

Click to navigate between pages

1-50 of 1000
...

Ellipsis Behavior

How page numbers are displayed for different positions

Near Start (page 1-4): Shows 1 2 3 4 5 ... 20

...

Middle (page 5-16): Shows 1 ... 9 10 11 ... 20

......

Near End (page 17-20): Shows 1 ... 16 17 18 19 20

...