Navigation
Pagination
A navigation control for paged data. It lives beside a table or list, never inside it. Controlled-by-attribute but self-updating: clicking moves the page (updating page) and emits nana-page-change so consumers can slice (client-side) or refetch (server-side).
Preview
Section titled “Preview”<nana-pagination page="1" total-pages="10"></nana-pagination> Installation
Section titled “Installation”Pagination ships for all three targets. The React and Svelte versions are a focused subset of the web component’s API — page numbers, layouts and sizes, without the page-size and jump controls. Pick the tab for your project — the CLI detects which you are on and serves the matching implementation.
npx @nana-tec/ui add paginationLands as src/components/ui/pagination/pagination.tsx, styled with Tailwind classes that
resolve to the design tokens. Requires Tailwind v4.
npx @nana-tec/ui add paginationLands in src/lib/components/ui/pagination/ as Pagination.svelte, styled with Tailwind
classes that resolve to the design tokens. Requires Tailwind v4.
Install the package for a versioned dependency:
npm install @nana-tec/ui-componentsOr copy the source in and own it:
npx @nana-tec/ui add paginationimport { Pagination } from "@/components/ui/pagination";
export function Example() { const [page, setPage] = useState(1);
return ( <Pagination page={page} totalPages={12} onPageChange={setPage} /> );}It is fully controlled: it renders page and reports the next one, and never
holds state of its own. layout="compact" swaps the numbers for a “Page 3 of
12” readout; layout="simple" leaves only Previous and Next.
siblingCount and boundaryCount control how much of a long range stays
visible before it collapses to an ellipsis — with the defaults, page 6 of 20
renders 1 … 5 6 7 … 20.
<script lang="ts"> import { Pagination } from '$lib/components/ui/pagination';
let page = $state(1);</script>
<Pagination {page} totalPages={12} onpagechange={(next) => (page = next)} />It is fully controlled: it renders page and reports the next one. Layouts are
numbered (the default), compact for a “Page 3 of 12” readout, and simple
for Previous and Next alone.
import "@nana-tec/ui-components/pagination";<nana-pagination page="1" total-pages="10"></nana-pagination>Classes you pass to the React and Svelte components are merged with
tailwind-merge, so an override wins rather than fighting. See
Using Tailwind.
Layouts
Section titled “Layouts”The layout attribute switches between numbered (default), compact, and
simple.
<nana-pagination
page="4"
total-pages="20"
layout="numbered"
show-first-last
></nana-pagination>
<nana-pagination page="4" total-pages="20" layout="compact"></nana-pagination>
<nana-pagination page="4" total-pages="20" layout="simple"></nana-pagination> Variants
Section titled “Variants”<nana-pagination page="6" total-pages="64" variant="default"></nana-pagination>
<nana-pagination page="6" total-pages="64" variant="soft"></nana-pagination>
<nana-pagination page="6" total-pages="64" variant="filled"></nana-pagination>
<nana-pagination page="6" total-pages="64" variant="pill"></nana-pagination>
<nana-pagination page="6" total-pages="64" variant="ghost"></nana-pagination> <nana-pagination
page="3"
total-pages="10"
size="sm"
variant="soft"
></nana-pagination>
<nana-pagination
page="3"
total-pages="10"
size="md"
variant="soft"
></nana-pagination>
<nana-pagination
page="3"
total-pages="10"
size="lg"
variant="soft"
></nana-pagination> The React and Svelte props, read from the components’ own types:
Pagination
| Prop | Type | Default |
|---|---|---|
pagerequired | number | |
totalPagesrequired | number | |
layout | PaginationLayout | 'numbered' |
size | PaginationSize | 'md' |
siblingCount | number | 1 |
boundaryCount | number | 1 |
disabled | boolean | false |
label | string | 'Pagination' |
onPageChange | (page: number) => void |
Also accepts Omit<HTMLAttributes<HTMLElement>, 'onChange'>.
Pagination
| Prop | Type | Default |
|---|---|---|
pagerequired | number | |
totalPagesrequired | number | |
layout | PaginationLayout | 'numbered' |
size | PaginationSize | 'md' |
siblingCount | number | 1 |
boundaryCount | number | 1 |
disabled | boolean | false |
label | string | 'Pagination' |
onpagechange | (page: number) => void |
Also accepts Omit<HTMLAttributes<HTMLElement>, 'class'>.
The attribute names, events and CSS hooks below are the Web Components reference.
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
boundaryCount | boundary-count | number | 1 | Page numbers pinned at each end |
disabled | disabled | boolean | false | Disable the whole control (e.g. while loading) |
layout | layout | PaginationLayout | 'numbered' | Structural layout |
page | page | number | 1 | Current page (1-based) |
pageSize | page-size | number | 10 | Rows per page |
pageSizeOptions | page-size-options | string | '10,25,50,100' | Comma-separated page-size options |
showFirstLast | show-first-last | boolean | false | Show « First / Last » buttons |
showJump | show-jump | boolean | false | Show the jump-to-page input |
showPageSize | show-page-size | boolean | false | Show the rows-per-page selector |
showResultCount | show-result-count | boolean | false | Show the "Showing X–Y of Z" summary |
siblingCount | sibling-count | number | 1 | Page numbers shown either side of the current page |
size | size | PaginationSize | 'md' | Control size |
totalItems | total-items | number | undefined | — | Total number of items — derives total pages and the result count |
totalPages | total-pages | number | undefined | — | Explicit total pages (used when totalItems is unknown) |
variant | variant | PaginationVariant | 'default' | Visual style |
Events
| Event | Description |
|---|---|
nana-page-change | detail: { page } |
nana-page-size-change | detail: { pageSize } |
CSS parts
| Part | Description |
|---|---|
::part(nav) | The controls container |
::part(ellipsis) | The truncation gap (…) |
CSS custom properties
| Property | Description |
|---|---|
--nana-pagination-radius | Corner radius of the page controls |
--nana-pagination-active-bg | Active page background (filled / pill) |
--nana-pagination-active-color | Active page text colour (filled / pill) |
Events
Section titled “Events”| Event | Type | Description |
|---|---|---|
nana-page-change | CustomEvent | detail: { page } |
nana-page-size-change | CustomEvent | detail: { pageSize } |
CSS Variables
Section titled “CSS Variables”| Variable | Description |
|---|---|
--nana-pagination-radius | Corner radius of the page controls |
--nana-pagination-active-bg | Active page background (filled / pill) |
--nana-pagination-active-color | Active page text colour (filled / pill) |
CSS Parts
Section titled “CSS Parts”| Part | Description |
|---|---|
nav | The controls container — every page-number and navigation button |
ellipsis | The truncation gap (…) between page runs |
Accessibility
Section titled “Accessibility”- Keyboard accessible
- Screen-reader labels
- Focus-visible states
- ARIA roles and states
- WCAG 2.2 AA contrast