Pagination
paginationNavigates controlled paged data with page ranges and ellipses.
Usage
Basic usage
Controlled component: holds page status by itself, onPageChange writes back. Click the page number/to switch between previous and next pages.
const [page, setPage] = useState(1);
<Pagination page={page} total={10} onPageChange={setPage} />Omit both sides
When the total number of pages is large, the outside of both sides of the current page will be automatically folded into ellipses, and the first and last pages will always be displayed.
<Pagination page={page} total={20} onPageChange={setPage} />Jump to the first and last pages
showFirstLast Displays the "Go to Home/Last Page" double arrow button.
<Pagination page={page} total={20} onPageChange={setPage} showFirstLast />Wider window
siblingCount Controls the number of page numbers displayed on the left and right of the current page, the default is 1.
<Pagination page={page} total={20} onPageChange={setPage} siblingCount={2} />Items per page
The switcher renders only when pageSizeOptions and onPageSizeChange are both supplied. If the current page falls outside the new page count after a switch, the component fires onPageChange as well, clamping to the new last page.
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(20);
<Pagination
page={page}
totalItems={5151}
pageSize={pageSize}
onPageChange={setPage}
pageSizeOptions={[20, 50, 100]}
onPageSizeChange={setPageSize}
showTotal
/>Disabled
disabled disables the entire pager and all buttons are unclickable.
<Pagination page={page} total={10} onPageChange={setPage} disabled />When to use
Use Pagination to move through a list or table when the total page count is known. Use Breadcrumb to communicate hierarchy or Tabs to switch peer content. Import getPaginationRange separately when you need the visible page-number sequence but want to render your own controls.
Import
import { Pagination, getPaginationRange } from "@hulianui/ui"Props
Pagination is controlled only: store page externally and update it from onPageChange.
| Name | Type | Default | Description |
|---|---|---|---|
| page* | number | - | Controlled current page, starting at 1. |
| total | number | - | Total number of pages, not items. Mutually exclusive with totalItems; this prop wins when both are supplied. |
| totalItems | number | - | Total number of items, matching the common data.total API meaning. Used with pageSize to derive pages. |
| pageSize | number | 10 | Items per page; used only with totalItems. |
| siblingCount | number | 1 | Visible page numbers on each side of the current page. |
| showFirstLast | boolean | false | Whether to show first-page and last-page buttons. |
| showTotal | boolean | (totalItems, [from, to]) => ReactNode | false | Total-items summary on the left. The built-in Chinese format means “N items total.” Requires totalItems and silently renders nothing when only total is provided. |
| showQuickJumper | boolean | false | Whether to show the page-jump input. Enter and blur submit, clamped to the valid range. |
| pageSizeOptions | number[] | - | Page-size choices, mirroring el-pagination's page-sizes. The switcher renders only when `onPageSizeChange` is supplied as well; supplying just one of the two renders nothing. |
| disabled | boolean | false | Whether all pagination controls are disabled. |
Events
| Event | Type | Description |
|---|---|---|
| onPageChange* | (page: number) => void | Called by page, previous/next, and first/last controls with a value clamped to [1, total]. |
| onPageSizeChange | (pageSize: number) => void | Called with the new page size. The component does not own pageSize, but it does own page repositioning: when totalItems is available and the current page falls outside the new page count, it fires onPageChange as well, clamping to the new last page rather than resetting to page 1. |
Example
function Demo() {
const [page, setPage] = useState(1);
return <Pagination page={page} total={20} onPageChange={setPage} />;
}With first/last controls and a wider page window:
<Pagination page={page} total={20} onPageChange={setPage} siblingCount={2} showFirstLast />With an API envelope whose total is an item count:
// res.data = { list, total: 128 }
<Pagination
page={page}
totalItems={res.data.total}
pageSize={20}
onPageChange={setPage}
showTotal
showQuickJumper
/>Page-size switcher
function Demo() {
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(20);
return (
<Pagination
page={page}
totalItems={5151}
pageSize={pageSize}
onPageChange={setPage}
pageSizeOptions={[20, 50, 100]}
onPageSizeChange={setPageSize}
showTotal
/>
);
}Usage guidelines
- [[pagination-range-single-gap-fill-not-ellipsis]]:
getPaginationRangeinserts the missing page number when a gap hides exactly one page. It uses an ellipsis only when the gap is greater than one, avoiding awkward output such as1 … 3. This follows MUI'susePaginationmodel. - The component has no internal page state. If
onPageChangedoes not updatepage, the controls appear unresponsive. - `total` means pages, unlike the item count commonly named `total` by APIs. Prefer
totalItemswithpageSizefor API data instead of duplicatingMath.ceilat call sites. Passing both props warns in development and usestotal. - The
totalsemantics are reserved for a breaking correction in 1.0, when the two props can be consolidated. PrefertotalItemsin new code. showTotalrequirestotalItems; page count alone cannot produce an item count, so the summary is omitted rather than throwing.- Changing the page size can fire two callbacks for one interaction:
onPageSizeChangewith the new size, thenonPageChangewith the clamped page when the current page no longer exists. Write both state updates as usual; React batches them into one render. When onlytotal(page count) is supplied, the new page count cannot be derived, so noonPageChangefollows and repositioning is left to you. pageSizeOptionsandonPageSizeChangemust both be present for the switcher to render: options without a callback means the change has nowhere to go, and a callback without options has nothing to switch between. It renders nothing rather than throwing, matchingshowTotal.- The switcher uses the library Select. It is the same implementation as the switcher in the ProTable footer, so appearance, copy, and accessible name match in both places.
- Built-in button and jumper labels are Chinese copy:
"\u8df3\u5230\u9996\u9875"(“Go to first page”),"\u4e0a\u4e00\u9875"(“Previous page”),"\u4e0b\u4e00\u9875"(“Next page”),"\u8df3\u5230\u672b\u9875"(“Go to last page”), and"\u8df3\u81f3\u7b2c\u51e0\u9875"(“Page to jump to”). Supply a customshowTotalrenderer when the built-in Chinese"\u5171 N \u6761"format is not appropriate.
Related
Playground
<Pagination page={page} total={10} onPageChange={setPage} />