ComponentsPagination
Pagination
Let users move between pages of results.
On this page
Install component source from the public registry, or use the local workspace package. Follow the setup guide first. The npm package is not published yet.
View registry filesImplemented with React and browser APIs, without an additional behavior or rendering library.
Shared foundation: React, clsx for conditional classes, tailwind-merge for resolving utility-class conflicts. Styles use Tailwind CSS and Dethink CSS variables.
Based on this component’s source and registry composition. Libraries used only in showcase examples are excluded.
Import the component into your page or component file.
import {
Pagination,
getPaginationRenderItems,
} from "@dethink/components";Try the examples, then open the code to use them in your app.
Bounded callback controls
Known totals expose previous, next, first, last, current-page state, and deterministic ellipses.
Show sourceexamples/pagination/basic.tsx
"use client";
import { useState } from "react";
import { Pagination } from "@dethink/components";
export function PaginationBasic() {
const [page, setPage] = useState(4);
return (
<Pagination
aria-label="Example result pages"
page={page}
pageCount={12}
showFirstLast
onPageChange={setPage}
/>
);
}Route-backed links
Real page URLs with in-place navigation: the selected page follows the URL, browser Back/Forward works, and changing pages preserves your scroll position.
Show sourceexamples/pagination/link-mode.tsx
"use client";
import { Pagination } from "@dethink/components";
import { usePathname, useSearchParams } from "next/navigation";
import { Suspense } from "react";
function LinkedPagination() {
const pathname = usePathname();
const searchParams = useSearchParams();
const requestedPage = Number(searchParams.get("page") ?? 6);
const page =
Number.isSafeInteger(requestedPage) && requestedPage > 0
? Math.min(requestedPage, 18)
: 6;
return (
<Pagination
aria-label="URL result pages"
hrefForPage={(nextPage) => {
const params = new URLSearchParams(searchParams);
params.set("page", String(nextPage));
return `${pathname}?${params}`;
}}
onClick={(event) => {
// Keep copy-link, new-tab, and modified-click behavior native.
if (
event.defaultPrevented ||
event.button !== 0 ||
event.metaKey ||
event.ctrlKey ||
event.shiftKey ||
event.altKey
)
return;
const link =
event.target instanceof Element
? event.target.closest("a[href]")
: null;
if (
!(link instanceof HTMLAnchorElement) ||
link.hasAttribute("download") ||
(link.target && link.target !== "_self")
)
return;
event.preventDefault();
const url = new URL(link.href);
url.hash = window.location.hash;
if (url.href !== window.location.href) {
// This demo owns local URL state; no server data needs refetching.
// Next.js synchronizes useSearchParams with the native history API.
window.history.pushState(null, "", url);
}
}}
page={page}
pageCount={18}
showFirstLast
/>
);
}
export function PaginationLinkMode() {
return (
<Suspense
fallback={
<Pagination
aria-label="URL result pages"
page={6}
pageCount={18}
showFirstLast
/>
}
>
<LinkedPagination />
</Suspense>
);
}Narrow and RTL
Small hosts collapse to Back/Next controls while preserving logical RTL placement for the page summary.
Saved searches
Small hosts collapse to Back/Next while RTL keeps the page summary at inline-start.
Show sourceexamples/pagination/responsive-card.tsx
"use client";
import { useState } from "react";
import { Pagination } from "@dethink/components";
export function PaginationResponsiveCard() {
const [page, setPage] = useState(5);
return (
<div className="border-border bg-background mx-auto max-w-xs rounded-lg border p-4 shadow-sm">
<div className="space-y-1">
<h4 className="text-foreground text-sm font-semibold">
Saved searches
</h4>
<p className="text-muted-foreground text-sm leading-6">
Small hosts collapse to Back/Next while RTL keeps the page summary at
inline-start.
</p>
</div>
<div className="mt-4" dir="rtl">
<Pagination
aria-label="Saved search pages"
page={page}
pageCount={12}
showFirstLast
onPageChange={setPage}
/>
</div>
</div>
);
}Examples that combine components for common tasks.
Table footer
Pagination composes beside row-count copy without owning table state, page size, or fetching.
Rows 21-30 of 120
Choose the mode that matches the data source and route model.
Callback mode
Use onPageChange when the current page lives in client state, a table model, or a server action wrapper.
Link mode
Use hrefForPage for real URLs. Integrate your router to avoid document reloads. This local-state demo uses Next.js history integration; server-backed results should navigate through the router with scrolling disabled.
Unbounded mode
Omit pageCount and pass hasNextPage for cursor-backed APIs that can page forward but do not know the final page.
Responsive layout
Pagination adapts to its own container width: small hosts use a Back/Next fallback, and larger hosts restore the normal page window.
Pagination renders a nav landmark and generated controls by default, with compound anatomy available for custom composition.
| Prop | What it does | Default |
|---|---|---|
pagenumber | Current one-based page. Values are clamped to the known pageCount when pageCount is supplied. | Not set |
pageCountnumber | Known total pages for bounded pagination. Omit for cursor-style or unknown-total lists. | Not set |
hasNextPageboolean | Enables next-page controls in unbounded mode without inventing a final page. | false |
onPageChange(page: number) => void | Callback mode for client-owned state updates. Generated controls render as buttons. | Not set |
hrefForPage(page: number) => string | undefined | Link mode for route-backed pagination. Targets without URLs fall back to onPageChange or render disabled. | Not set |
compactboolean | Reduces page-window density for cards, mobile layouts, and table footers. | false |
showFirstLast / hideDisabledControlsboolean | Adds first/last controls and chooses whether impossible boundary controls are disabled or hidden. | false / false |
siblingCount / boundaryCountnumber | Controls how many pages render around the current page and at the known page boundaries. | 1 / 1 |
size"sm" | "md" | "lg" | Tokenized control size for dense tables, default pages, or larger touch targets. | "md" |
statusReactNode | false | Visible page summary. Pass false when the surrounding UI already provides the status text. | generated |
labelsPaginationLabels | Custom accessible labels for the nav landmark, page controls, boundary controls, ellipses, status, and narrow Back/Next text. | built-in English labels |
childrenReactNode | Compound anatomy escape hatch when consumers need full manual composition. | Not set |