Pagination
href is a function from page number to URL, so pagination works for /blog/2 and /blog?page=2 alike. That makes every page a real link — crawlable, openable in a new tab, and working with no JavaScript, which is what a static site wants.
Basic pagination
pagination({ page: 1, count: 5, href: (page) => '#page-' + page })Windowing
The first and last pages are always shown, plus a window around the current one, with an ellipsis wherever the sequence jumps.
stack({ gap: 'md' },
pagination({ page: 1, count: 12, href: (page) => '#a-' + page }),
pagination({ page: 6, count: 12, href: (page) => '#b-' + page }),
pagination({ page: 12, count: 12, href: (page) => '#c-' + page }),
)Siblings
siblings is how many pages sit either side of the current one.
stack({ gap: 'md' },
pagination({ page: 6, count: 12, siblings: 0, href: (page) => '#d-' + page }),
pagination({ page: 6, count: 12, siblings: 1, href: (page) => '#e-' + page }),
pagination({ page: 6, count: 12, siblings: 3, href: (page) => '#f-' + page }),
)Ends
Previous is disabled on the first page and next on the last, so the control never offers a page that does not exist.
stack({ gap: 'md' },
pagination({ page: 1, count: 3, href: (page) => '#g-' + page }),
pagination({ page: 3, count: 3, href: (page) => '#h-' + page }),
pagination({ page: 1, count: 1, href: (page) => '#i-' + page }),
)Colors and labels
stack({ gap: 'md' },
pagination({ page: 3, count: 6, color: 'neutral', href: (page) => '#j-' + page }),
pagination({
page: 3,
count: 6,
color: 'success',
previousLabel: 'Newer',
nextLabel: 'Older',
href: (page) => '#k-' + page,
}),
)Without hrefs
With no href, the numbers render as buttons carrying data-su-page — for a page that filters in place with its own script. Prefer links when you can: they survive JavaScript being off.
pagination({ page: 2, count: 4 })In a blog
The usual shape on a static site: generateStaticParams produces one page per slice, and href points at them.
Showing 21–30 of 47
return (() => {
const posts = 47
const perPage = 10
const current = 3
return stack({ gap: 'md', align: 'center' },
text({ variant: 'small', tone: 'muted' },
'Showing ' + ((current - 1) * perPage + 1) + '–' + Math.min(current * perPage, posts) + ' of ' + posts,
),
pagination({
page: current,
count: Math.ceil(posts / perPage),
href: (page) => page === 1 ? '#blog' : '#blog-' + page,
}),
)
})()Props
| Prop | Type | Default | Description |
|---|---|---|---|
page | number | 1 | The current page. Clamped into range. |
count | number | 1 | How many pages there are. |
href | (page: number) => string | — | Page number to URL. Without it, pages render as buttons. |
siblings | number | 1 | Pages shown either side of the current one. |
color | 'primary' | 'neutral' | 'success' | 'warning' | 'danger' | 'primary' | Colour of the current page. |
label | string | 'Pagination' | Accessible name for the nav landmark. |
previousLabel | Child | '‹' | Content of the previous control. |
nextLabel | Child | '›' | Content of the next control. |