Pagination
Overview
Pagination is used for splitting up content or data into several pages, with a control for navigating to the next or previous page.
Features
- Data-driven compact composition —
SPaginationdelegates the whole structure to the AriaPaginationCompact, which owns the page windowing, default content, and internal composition (root → list → page items / actions → ellipsis). - Controlled or uncontrolled page state —
page/pageSizesupportv-model(controlled), whiledefaultPage/defaultPageSizeprovide uncontrolled usage backed byuseControllableState. - Smart page windowing —
siblingCountcontrols how many page buttons surround the current page;showEdges: truealways pins the first and last page and renders single or double ellipsis depending on where the current page sits. - Boundary auto-disable — prev/first are disabled on the first page and next/last on the last page;
disabled: truedisables the entire control and suppresses all interaction. - Four visual variants —
variant(pure/solid/outline/soft),shape(rounded/square), andsize(xs…2xl) are applied through thepaginationVariantsscv()recipe. actionVariant— when enabled, the first/prev/next/last action buttons also take the currentvariantstyling, which otherwise paints only the current page.- Eight customization slots —
default(list),leading,trailing,first,prev,next,last, andellipsis; dynamic slot forwarding keeps every Aria slot reachable from the styled wrapper. - Per-region props forwarding —
listProps,listItemProps,ellipsisProps,firstProps,prevProps,nextProps,lastPropsforward attributes to each region. - Full ARIA semantics — the root is a
nav; the active page carriesaria-current="page"and the selected state attribute; every button gets a localizedaria-label. - Localized accessibility text — action labels and the page label template come from the locale registry (
pageLabelsupports the{value}placeholder) across 13 built-in languages. - RTL support — action icons are mirrored automatically via
rtl:[&>svg]:rotate-180. - Aria composition —
PaginationRoot/PaginationList/PaginationListItem/PaginationEllipsis/PaginationFirst/PaginationPrev/PaginationNext/PaginationLastare exported from@vean/aria/paginationfor fully custom styled builds.
Usage
SPaginationdelegates its page windowing to AriaPaginationCompact. For unstyled, data-driven composition, importPaginationCompactfrom@vean/aria/pagination.
Demos
Basic
Slot
You can use the slot to customize the first, prev, next, and last buttons
Action
When actionVariant is true, the action button will use the variant styling
Show
When showFirstOrLast is false, the first and last buttons will not be shown
API
Aria Layer
11Pagination
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | Additional class names applied to the root element. |
ui | Partial<PaginationUi> | — | The custom ui class names |
size | ThemeSize | — | The size of the pagination |
variant | PaginationVariant | — | The variant of the pagination |
shape | PaginationShape | — | The shape of the pagination |
actionVariant | boolean | — | Whether the first/prev/next/last action buttons also use the `variant` styling, which otherwise only paints the current page |
showFirstOrLast | boolean | — | Whether to show a first or last. |
listProps | PaginationListProps | — | Properties forwarded to the list element. |
listItemProps | PaginationListItemProps | — | Properties forwarded to the list item element. |
ellipsisProps | PaginationEllipsisProps | — | Properties forwarded to the ellipsis element. |
firstProps | PaginationButtonProps | — | Properties forwarded to the first element. |
prevProps | PaginationButtonProps | — | Properties forwarded to the prev element. |
nextProps | PaginationButtonProps | — | Properties forwarded to the next element. |
lastProps | PaginationButtonProps | — | Properties forwarded to the last element. |
page | number | — | The controlled value of the current page. Can be bound as `v-model:page`. |
defaultPage | number | — | The value of the page that should be active when initially rendered. Use when you do not need to control the value
state. |
pageSize | number | — | Number of items per page |
defaultPageSize | number | 10 | The default value of `pageSize` when initially rendered. Use when you do not need to control the value state. |
total | number | — | Number of items in your list |
siblingCount | number | — | Number of sibling should be shown around the current page |
disabled | boolean | — | When `true`, prevents the user from interacting with item |
showEdges | boolean | — | When `true`, always show first page, last page, and ellipsis |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:page | number | Event handler called when the page value changes |
update:pageSize | number | Event handler called when the page size value changes |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
leading | — | Custom content for the leading slot. |
trailing | — | Custom content for the trailing slot. |
first | — | Custom content for the first slot. |
prev | — | Custom content for the prev slot. |
next | — | Custom content for the next slot. |
last | — | Custom content for the last slot. |
ellipsis | — | Custom content for the ellipsis slot. |
PaginationButton
Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | ButtonType | 'button' | The type of the button element. Can be one of 'button', 'submit', or 'reset'. |
disabled | boolean | — | Whether the component is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
PaginationCompact
Props
| Prop | Type | Default | Description |
|---|---|---|---|
showFirstOrLast | boolean | — | Whether to show a first or last. |
listProps | PaginationListProps | — | Properties forwarded to the list element. |
listItemProps | PaginationListItemProps | — | Properties forwarded to the list item element. |
ellipsisProps | PaginationEllipsisProps | — | Properties forwarded to the ellipsis element. |
firstProps | PaginationButtonProps | — | Properties forwarded to the first element. |
prevProps | PaginationButtonProps | — | Properties forwarded to the prev element. |
nextProps | PaginationButtonProps | — | Properties forwarded to the next element. |
lastProps | PaginationButtonProps | — | Properties forwarded to the last element. |
page | number | — | The controlled value of the current page. Can be bound as `v-model:page`. |
defaultPage | number | — | The value of the page that should be active when initially rendered. Use when you do not need to control the value
state. |
pageSize | number | — | Number of items per page |
defaultPageSize | number | 10 | The default value of `pageSize` when initially rendered. Use when you do not need to control the value state. |
total | number | — | Number of items in your list |
siblingCount | number | — | Number of sibling should be shown around the current page |
disabled | boolean | — | When `true`, prevents the user from interacting with item |
showEdges | boolean | — | When `true`, always show first page, last page, and ellipsis |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:page | number | Event handler called when the page value changes |
update:pageSize | number | Event handler called when the page size value changes |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
leading | — | Custom content for the leading slot. |
trailing | — | Custom content for the trailing slot. |
first | — | Custom content for the first slot. |
prev | — | Custom content for the prev slot. |
next | — | Custom content for the next slot. |
last | — | Custom content for the last slot. |
ellipsis | — | Custom content for the ellipsis slot. |
PaginationEllipsis
PaginationFirst
PaginationLast
PaginationList
PaginationListItem
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | — | Value for the page |
type | ButtonType | 'button' | The type of the button element. Can be one of 'button', 'submit', or 'reset'. |
disabled | boolean | — | Whether the component is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
PaginationNext
PaginationPrev
PaginationRoot
Props
| Prop | Type | Default | Description |
|---|---|---|---|
page | number | — | The controlled value of the current page. Can be bound as `v-model:page`. |
defaultPage | number | — | The value of the page that should be active when initially rendered. Use when you do not need to control the value
state. |
pageSize | number | — | Number of items per page |
defaultPageSize | number | 10 | The default value of `pageSize` when initially rendered. Use when you do not need to control the value state. |
total | number | — | Number of items in your list |
siblingCount | number | — | Number of sibling should be shown around the current page |
disabled | boolean | — | When `true`, prevents the user from interacting with item |
showEdges | boolean | — | When `true`, always show first page, last page, and ellipsis |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:page | number | Event handler called when the page value changes |
update:pageSize | number | Event handler called when the page size value changes |
Notes
Architecture and benchmark comparison
| Concern | SoybeanUI | shadcn-vue Pagination | Ant Design Pagination | Element Plus Pagination |
|---|---|---|---|---|
| Aria / styled separation | ✅ @vean/aria/pagination + scv() | ❌ single package | ❌ single package | ❌ single package |
| Compact data-driven API | ✅ PaginationCompact + 8 typed slots | ✅ Pagination + PaginationItem parts | ✅ config-driven (total, current) | ✅ layout / components |
| Controlled / uncontrolled | ✅ page/defaultPage, pageSize/defaultPageSize | ✅ v-model | ✅ current / defaultCurrent | ✅ v-model |
| Ellipsis + pinned edges | ✅ showEdges + automatic single/double ellipsis | ✅ showEdges on PaginationItem | ✅ showLessItems / auto ellipsis | ✅ pager-count |
| Boundary auto-disable | ✅ prev/first at page 1, next/last at last page | ✅ disabled on parts | ✅ prevIcon/nextIcon handling | ✅ auto |
| Disable entire control | ✅ disabled | ✅ | ✅ disabled | ✅ disabled |
| Selected page styling | ✅ data-[selected] + actionVariant | ✅ data-[active] | ✅ current item class | ✅ active |
Localized aria-label | ✅ locale registry (13 langs, {value} interpolation) | hard-coded / aria-label prop | partial | — |
| Variant system | ✅ pure/solid/outline/soft × rounded/square × xs…2xl | ✅ size only | ✅ size | ✅ small/default/large |
| Per-region props forwarding | ✅ 7 props groups | ✅ asChild per part | ✅ itemRender | ✅ per-part props |
| RTL icon mirroring | ✅ automatic | — | ✅ | ✅ |
Runtime considerations
- Window math —
getRangeshows both ellipsis only when the current page is far enough from both ends (leftSiblingIndex > firstPageIndex + 2andrightSiblingIndex < lastPageIndex - 2); near either edge it degrades to a single ellipsis, otherwise the full range renders. totalpages —pageCountisMath.max(1, Math.ceil(total / pageSize)), so an empty list still yields one page; apageabovepageCountrenders the last window without crashing.- Controlled state sync — when bound with
v-model:page, clicking an item emitsupdate:page; the internal window follows the prop, so external page changes (e.g. after a fetch) re-render the correct window automatically. showFirstOrLast— setting it tofalseremoves the first/last buttons but keeps prev/next; page count math is unaffected.actionVariant— it only lets the action buttons reuse thevariantrecipe; the selected page item always uses thedata-[selected]styling regardless of this flag.- Locale fallback — button labels come from
useLocaleMessages; missing keys fall back to the default English bundle;pageLabelsupports the{value}placeholder viainterpolate. - A11y output — the active page renders with
aria-current="page"and thedata-selectedattribute; disabled actions keepdisabledso they are skipped by assistive technology and keyboard navigation.
FAQ
When do the ellipsis appear?
With showEdges: true, the first and last page are always pinned. The current page sits at either edge of the window → one ellipsis; deep in the middle (e.g. page 50 of 100) → two ellipsis with siblingCount pages on each side. With showEdges: false, only the current window renders and no ellipsis is shown.
How do I make pagination controlled vs. uncontrolled?
Pass page (and optionally pageSize) together with an @update:page handler, or simply use v-model:page. For uncontrolled usage, rely on defaultPage / defaultPageSize and let the component manage its own state.
What is actionVariant for?
By default the first/prev/next/last action buttons use a neutral action style. When actionVariant is true, those buttons also take the variant styling — the same one the current page uses (e.g. a solid primary look) — which is useful when the active page visually merges with the action buttons.
Why are prev/first disabled on the first page?
The first/prev buttons disable automatically when page === 1 and next/last when page === pageCount, preventing out-of-range navigation. Set disabled: true to disable the whole control.
Can I fully customize the buttons?
Yes — the first, prev, next, last, ellipsis, leading, and trailing slots replace the default content, and firstProps / prevProps / nextProps / lastProps forward attributes (including styles) to the respective regions. The default list slot receives scoped { page, pageCount }-aware items through the Aria list.
How is accessibility handled?
The root is a nav landmark; every button carries a localized aria-label (the page buttons use the pageLabel template, e.g. “Page 5”); the active page is announced with aria-current="page"; disabled buttons use the native disabled attribute and are skipped by assistive technology.
Can I build a fully custom pagination?
Yes — compose PaginationRoot / PaginationList / PaginationListItem / PaginationEllipsis / PaginationFirst / PaginationPrev / PaginationNext / PaginationLast from @vean/aria/pagination and inject styles via providePaginationUi (or SPagination’s ui prop).