<Stack
width="100%"
attributes={{"style":{"overflowX":"auto"}}}
>
<Pagination
total={10}
defaultPage={5}
ariaLabel="Pagination playground"
previousAriaLabel="Previous page"
nextAriaLabel="Next page"
/>
</Stack>Features
- Offers numbered and compact layouts.
- Supports controlled and uncontrolled page selection.
- Validates typed page numbers before changing pages.
- Supports native links and router links.
Numbered Pages
Set total to the page count and defaultPage to the starting page, counted from 1. Use siblingCount for neighboring page numbers; the default is 1 on each side.
"use client";
import { Pagination } from "@vortexlabs/vortex";
export const PaginationNumberedExample = () => (
<Pagination
ariaLabel="Pagination: numbered"
previousAriaLabel="Previous page"
nextAriaLabel="Next page"
total={10}
defaultPage={5}
/>
);Compact Layout
Use variant="compact" for a page count between the arrows. Provide pageLabel and totalLabel for its text.
"use client";
import { Pagination } from "@vortexlabs/vortex";
export const PaginationCompactExample = () => (
<Pagination
ariaLabel="Pagination: compact text"
previousAriaLabel="Previous page"
nextAriaLabel="Next page"
variant="compact"
pageLabel="Page"
totalLabel={({ total }) => `of ${total}`}
pageInput={false}
total={10}
defaultPage={5}
/>
);Page Input
Set pageInput in compact mode to accept a page number on Enter. Provide its ariaLabel and errorMessage; invalid entries show the error without changing pages.
"use client";
import { Pagination } from "@vortexlabs/vortex";
export const PaginationInputExample = () => (
<Pagination
ariaLabel="Pagination: page input and validation"
previousAriaLabel="Previous page"
nextAriaLabel="Next page"
variant="compact"
pageLabel="Page"
totalLabel={({ total }) => `of ${total}`}
pageInput={{
ariaLabel: "Page number",
errorMessage: ({ total }) => `Enter a whole number from 1 to ${total}.`,
}}
total={2500}
defaultPage={1000}
/>
);With one page, the input becomes text.
Controlled Selection
Use page with onChange to control selection. Update page in the handler and load or display its content in your app.
"use client";
import { Button, Pagination, Stack, Text } from "@vortexlabs/vortex";
import React from "react";
export const PaginationControlledExample = () => {
const [page, setPage] = React.useState(3);
return (
<Stack gap={3}>
<Text size="body-sm-desktop" attributes={{ "aria-live": "polite" }}>
Selected page: {page}
</Text>
<Pagination
ariaLabel="Pagination: controlled"
previousAriaLabel="Previous page"
nextAriaLabel="Next page"
total={8}
page={page}
onChange={({ page }) => setPage(page)}
/>
<Stack direction="row">
<Button variant="secondary" size="sm" onClick={() => setPage(1)}>
Reset to page 1
</Button>
</Stack>
</Stack>
);
};Link Navigation
Use href for page URLs and page for the current page. Omit onChange and defaultPage in link mode.
Use renderLink for router links; forward all supplied attributes, including ref and children.
"use client";
import { Pagination } from "@vortexlabs/vortex";
import NextLink from "next/link";
import { usePathname, useSearchParams } from "next/navigation";
import { Suspense } from "react";
const PaginationLinks = () => {
const pathname = usePathname();
const searchParams = useSearchParams();
const requestedPage = Number(searchParams.get("page") ?? 1);
const page = Number.isInteger(requestedPage)
? Math.min(5, Math.max(1, requestedPage))
: 1;
return (
<Pagination
total={5}
page={page}
href={({ page }) => `${pathname}?page=${page}#link-navigation`}
renderLink={(attributes) => <NextLink {...attributes} />}
ariaLabel="Pagination: linked pages"
previousAriaLabel="Previous page"
nextAriaLabel="Next page"
/>
);
};
export const PaginationLinksExample = () => (
<Suspense fallback={<span>Loading pagination…</span>}>
<PaginationLinks />
</Suspense>
);Omit renderLink for native links. In link mode, page input submissions use window.location.assign, even with a custom renderLink.
Full Width
Set fullWidth in numbered mode to place the arrows at the container's edges.
"use client";
import { Pagination } from "@vortexlabs/vortex";
export const PaginationWidthExample = () => (
<Pagination
ariaLabel="Pagination: full width"
previousAriaLabel="Previous page"
nextAriaLabel="Next page"
total={10}
defaultPage={5}
fullWidth
/>
);Table Footer
Set total to Math.ceil(totalRows / rowsPerPage) for a table footer. Reset page to 1 when changing the page size.
"use client";
import { Pagination, Select, Stack, Text } from "@vortexlabs/vortex";
import React from "react";
export const PaginationFooterExample = () => {
const [page, setPage] = React.useState(5);
const [rowsPerPage, setRowsPerPage] = React.useState(25);
const totalRows = 2500;
const firstRow = (page - 1) * rowsPerPage + 1;
const lastRow = Math.min(page * rowsPerPage, totalRows);
return (
<Stack
direction="row"
align="start"
justify="space-between"
gap={4}
padding={4}
borderColor="default"
attributes={{ style: { borderColor: "var(--vortex-color-border-divider)" } }}
wrap
>
<Stack minHeight={10} justify="center">
<Text size="body-sm-desktop" tabularNums>
{firstRow}–{lastRow} of {totalRows.toLocaleString("en")} rows
</Text>
</Stack>
<Stack direction="row" align="start" gap={5} wrap>
<Select.Root width="auto">
<Stack direction="row" align="center" gap={2}>
<Select.Label marginBottom={0} wrap="nowrap">
<Text as="span" size="body-sm-desktop" weight="regular">
Rows per page
</Text>
</Select.Label>
<Stack width="72px">
<Select.Field value={String(rowsPerPage)}>
{[10, 25, 50].map((size) => (
<Select.Item
key={size}
selected={size === rowsPerPage}
onClick={() => {
setRowsPerPage(size);
setPage(1);
}}
>
{size}
</Select.Item>
))}
</Select.Field>
</Stack>
</Stack>
</Select.Root>
<Pagination
ariaLabel="Pagination: large dataset"
previousAriaLabel="Previous page"
nextAriaLabel="Next page"
variant="compact"
pageLabel="Page"
totalLabel={({ total }) => `of ${total}`}
pageInput={{
ariaLabel: "Page number",
errorMessage: ({ total }) => `Enter a whole number from 1 to ${total}.`,
}}
total={Math.ceil(totalRows / rowsPerPage)}
page={page}
onChange={({ page }) => setPage(page)}
/>
</Stack>
</Stack>
);
};Accessibility
ariaLabel to identify the pagination, such as ‘Search results pages’.previousLabel and nextLabel, or previousAriaLabel and nextAriaLabel for icon-only arrows.pageInput.ariaLabel and pageInput.errorMessage when enabling page input.