Learn the concept
Advanced Patterns
A reusable Pagination component manages page state, renders page numbers with ellipsis for large ranges, and exposes both controlled and uncontrolled APIs. It should include keyboard navigation, ARIA attributes for accessibility, and integrate cleanly with server-side pagination endpoints.
Component API Design:
totalItems / totalPages — total count from the data sourcepageSize — items per page (often configurable)currentPage (controlled) or defaultPage (uncontrolled) — active pageonPageChange — callback when page changessiblingCount — how many page numbers to show around the current pageboundaryCount — how many page numbers to show at the start and endPage Range Algorithm:
[1, '...', 4, 5, 6, '...', 20] from the current page and total pagesAccessibility (ARIA):
<nav aria-label="Pagination"> as the wrapper<ol> / <ul> for the page listaria-current="page"aria-disabledaria-label on each button (e.g., "Go to page 5")Controlled vs Uncontrolled:
currentPage state and passes it as a prop. Component calls onPageChange but does not update internally.defaultPage as the initial value.Server-Side Pagination:
{ data, total, page, pageSize }totalPages = Math.ceil(total / pageSize)totalPages and currentPage to the Pagination componentfunction generatePageRange({ currentPage, totalPages, siblingCount = 1 }) {
const ELLIPSIS = '...';
function range(start, end) {
return Array.from({ length: end - start + 1 }, (_, i) => start + i);
}
// Total slots: first + last + current + 2*siblings + 2*ellipsis
const totalSlots = siblingCount * 2 + 5;
// Case 1: Total pages fit within slots — no ellipsis
if (totalPages <= totalSlots) {
return range(1, totalPages);
}
const leftSibling = Math.max(currentPage - siblingCount, 1);
const rightSibling = Math.min(currentPage + siblingCount, totalPages);
const showLeftEllipsis = leftSibling > 2;
const showRightEllipsis = rightSibling < totalPages - 1;
// Case 2: No left ellipsis, show right ellipsis
if (!showLeftEllipsis && showRightEllipsis) {
const leftRange = range(1, 3 + siblingCount * 2);
return [...leftRange, ELLIPSIS, totalPages];
}
// Case 3: Show left ellipsis, no right ellipsis
if (showLeftEllipsis && !showRightEllipsis) {
const rightRange = range(totalPages - (2 + siblingCount * 2), totalPages);
return [1, ELLIPSIS, ...rightRange];
}
// Case 4: Both ellipsis
const middleRange = range(leftSibling, rightSibling);
return [1, ELLIPSIS, ...middleRange, ELLIPSIS, totalPages];
}
// Examples:
// currentPage=1, totalPages=20 => [1, 2, 3, 4, 5, '...', 20]
// currentPage=10, totalPages=20 => [1, '...', 9, 10, 11, '...', 20]
// currentPage=20, totalPages=20 => [1, '...', 16, 17, 18, 19, 20]
// currentPage=3, totalPages=5 => [1, 2, 3, 4, 5]Paginating large product catalogs with server-side pagination, displaying page numbers and total result counts while keeping URLs shareable via query parameters
Paginating data tables with configurable page sizes, column sorting, and server-side filtering, where the pagination component coordinates with multiple data controls
Displaying paginated search results with ellipsis for large result sets, deep linking support so users can share or bookmark specific result pages
Build a reusable data table component with pagination, sortable columns, row selection, and configurable page sizes integrated with a mock REST API
Create a pagination component that reads from and writes to URL search parameters, supports browser back/forward navigation, and works with Next.js App Router