XDSPagination@xds/core · Pagination
Preview coming soon

Usage

Pagination lets users step through pages of content. Place it below a table, list, or card grid so users can move forward and backward through results. Pick a variant to match the context — numbered pages for data tables, a count for large lists, compact for tight spaces, or dots for carousels.

Best practices

GuidancePractices
DoPlace pagination below the content it controls so users see results before navigating.
DoUse the pages variant for data tables where users need to jump to a specific page.
DoUse the count variant with a page size selector when users need to control how many items they see at once.
DoUse the dots variant for carousels and walkthroughs where the total is small and position matters more than a number.
DoPass totalItems when the total is known so users can see how much content remains.
Don'tShow pagination when all items fit on a single page — there is nothing to paginate.
Don'tUse the dots variant for more than about 10 pages — the dots become too small to be useful.
Don'tPlace pagination above the content — users expect it at the bottom.

Import

ts
import {XDSPagination} from '@xds/core/Pagination'

Props

PropTypeDescription
pagerequired
numberCurrent page number (1-based). Page 1 is the first page.
onChangerequired
(page: number) => voidCalled when the page changes.
changeAction
(page: number) => void | Promise<void>Async action on page change. Fires after onChange and uses React transitions for built-in loading state.
totalItems
numberTotal number of items. Used to calculate page count. Takes precedence over totalPages if both provided.
totalPages
numberTotal number of pages. Use when you know page count but not item count.
hasMore
booleanWhether more pages exist after the current one. Use for cursor-based pagination where total is unknown.
pageSize
number (default: 10)Number of items per page.
pageSizeOptions
number[]Available page size options. Shows a page size selector dropdown when provided.
onPageSizeChange
(pageSize: number) => voidCalled when the page size changes. Automatically resets to page 1.
variant
'pages' | 'count' | 'compact' | 'dots' | 'none' (default: 'pages')Visual variant controlling what appears between prev/next buttons. 'pages' shows page number buttons with ellipsis, 'count' shows 'X-Y of Z' text, 'compact' shows 'Page X of Y', 'dots' shows dot indicators, 'none' shows just prev/next buttons.
siblingCount
number (default: 1)Number of page buttons to show on each side of the current page. Only applies when variant='pages'.
size
'sm' | 'md' (default: 'md')Size of the pagination controls.
isDisabled
boolean (default: false)Whether the component is disabled.
label
string (default: 'Pagination')Accessible label for the navigation landmark.
xstyle
StyleXStylesStyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value — not an inline style object like style={{}}.

Examples

Common configurations, variations, and states.
Pagination — Dots Carousel
tsx
'use client';
import {useState} from 'react';
import {XDSPagination} from '@xds/core/Pagination';
import {XDSCard} from '@xds/core/Card';
import {XDSText} from '@xds/core/Text';
import {XDSStack} from '@xds/core/Layout';
import {XDSAvatar} from '@xds/core/Avatar';
import {XDSIcon} from '@xds/core/Icon';
import {StarIcon} from '@heroicons/react/24/solid';
import * as stylex from '@stylexjs/stylex';
const REVIEWS = [
{
name: 'Jeannie Grant',
date: 'June 01, 2025',
stars: 5,
quote:
'A thorough report was done on our financial situation. Better deals were found and processed on our behalf, which took a lot of stress away.',
},
{
name: 'Derval Russell',
date: 'November 09, 2025',
stars: 5,
quote:
'I have been a client for 8 years now and have always found the advice provided excellent. They take the time to explain things clearly.',
},
{
name: 'Claire Dawson',
date: 'October 15, 2025',
stars: 5,
quote:
'Constantly professional and concise. Our mortgage process was smooth from start to finish thanks to their dedicated team.',
},
{
name: 'Marcus Webb',
date: 'September 22, 2025',
stars: 4,
quote:
'Great service overall. The team was responsive and knowledgeable. Would definitely recommend to anyone looking for solid financial advice.',
},
];
const styles = stylex.create({
root: {
maxWidth: 480,
width: '100%',
},
pagination: {
justifyContent: 'center',
paddingTop: 4,
},
});
function Stars({count}: {count: number}) {
return (
<XDSStack direction="horizontal" gap={0}>
{Array.from({length: count}, (_, i) => (
<XDSIcon key={i} icon={StarIcon} size="sm" color="warning" />
))}
</XDSStack>
);
}
export default function PaginationDotsCarousel() {
const [page, setPage] = useState(1);
const review = REVIEWS[page - 1];
return (
<XDSStack direction="vertical" gap={3} xstyle={styles.root}>
<XDSCard padding={5}>
<XDSStack direction="vertical" gap={3}>
<Stars count={review.stars} />
<XDSText type="body">{review.quote}</XDSText>
<XDSStack
direction="horizontal"
gap={3}
vAlign="center"
hAlign="start">
<XDSAvatar name={review.name} size="small" />
<XDSStack direction="vertical" gap={0}>
<XDSText type="supporting" weight="bold">
{review.name}
</XDSText>
<XDSText type="supporting" color="secondary">
{review.date}
</XDSText>
</XDSStack>
</XDSStack>
</XDSStack>
</XDSCard>
<XDSPagination
page={page}
onChange={setPage}
totalPages={REVIEWS.length}
variant="dots"
xstyle={styles.pagination}
/>
</XDSStack>
);
}
Pagination — Page Size SelectorA transactions table with pagination and a page size dropdown at the bottom. Shows how pagination works as a footer below real content, with adjustable rows per page.
tsx
'use client';
import {useState} from 'react';
import {XDSPagination} from '@xds/core/Pagination';
import {XDSHeading} from '@xds/core/Text';
import {XDSStack} from '@xds/core/Layout';
import {XDSTable} from '@xds/core/Table';
import * as stylex from '@stylexjs/stylex';
const styles = stylex.create({
root: {
width: '100%',
},
pagination: {
paddingTop: 8,
flexDirection: 'row-reverse',
},
});
const DATA = [
{
id: '1',
date: 'Apr 18',
description: 'Payment received',
amount: '$2,450.00',
},
{
id: '2',
date: 'Apr 15',
description: 'Subscription renewal',
amount: '$99.00',
},
{id: '3', date: 'Apr 12', description: 'Refund issued', amount: '-$180.00'},
{id: '4', date: 'Apr 10', description: 'Invoice paid', amount: '$1,200.00'},
];
export default function PaginationPageSize() {
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
return (
<XDSStack direction="vertical" xstyle={styles.root}>
<XDSHeading level={4}>Transactions</XDSHeading>
<XDSTable
idKey="id"
columns={[
{key: 'date', header: 'Date'},
{key: 'description', header: 'Description'},
{key: 'amount', header: 'Amount'},
]}
data={DATA}
/>
<XDSPagination
page={page}
onChange={setPage}
totalItems={350}
pageSize={pageSize}
onPageSizeChange={setPageSize}
pageSizeOptions={[10, 25, 50, 100]}
variant="count"
xstyle={styles.pagination}
/>
</XDSStack>
);
}
Pagination — With TablePagination below a data table with client-side page slicing. Use the count variant with small size for dense data views where users need to see item ranges.
tsx
'use client';
import {useState} from 'react';
import {XDSPagination} from '@xds/core/Pagination';
import {XDSTable} from '@xds/core/Table';
import {XDSStack} from '@xds/core/Layout';
import * as stylex from '@stylexjs/stylex';
const styles = stylex.create({
root: {
width: '100%',
},
pagination: {
justifyContent: 'center',
paddingTop: 8,
},
});
const ALL_DATA = [
{id: '1', name: 'Olivia Chen', role: 'Engineer', status: 'Active'},
{id: '2', name: 'Marcus Rivera', role: 'Designer', status: 'Active'},
{id: '3', name: 'Aisha Patel', role: 'Marketing', status: 'Invited'},
{id: '4', name: 'James Okafor', role: 'Engineer', status: 'Active'},
{id: '5', name: 'Sofia Nguyen', role: 'Sales', status: 'Active'},
{id: '6', name: 'Liam Johansson', role: 'Engineer', status: 'Inactive'},
{id: '7', name: 'Elena Kowalski', role: 'Designer', status: 'Active'},
{id: '8', name: 'David Kim', role: 'Marketing', status: 'Active'},
{id: '9', name: 'Priya Sharma', role: 'Sales', status: 'Invited'},
{id: '10', name: 'Noah Tanaka', role: 'Engineer', status: 'Active'},
{id: '11', name: 'Fatima Al-Rashid', role: 'Designer', status: 'Active'},
{id: '12', name: 'Carlos Mendez', role: 'Marketing', status: 'Inactive'},
];
const PAGE_SIZE = 4;
export default function PaginationWithTable() {
const [page, setPage] = useState(1);
const start = (page - 1) * PAGE_SIZE;
const pageData = ALL_DATA.slice(start, start + PAGE_SIZE);
return (
<XDSStack direction="vertical" xstyle={styles.root}>
<XDSTable
idKey="id"
columns={[
{key: 'name', header: 'Name'},
{key: 'role', header: 'Role'},
{key: 'status', header: 'Status'},
]}
data={pageData}
/>
<XDSPagination
page={page}
onChange={setPage}
totalItems={ALL_DATA.length}
pageSize={PAGE_SIZE}
variant="count"
size="sm"
xstyle={styles.pagination}
/>
</XDSStack>
);
}

Showcase source

tsx
'use client';
import {useState} from 'react';
import {XDSPagination} from '@xds/core/Pagination';
import {XDSStack} from '@xds/core/Layout';
import * as stylex from '@stylexjs/stylex';
const styles = stylex.create({
root: {
width: '100%',
},
});
export default function PaginationVariants() {
const [pagesPage, setPagesPage] = useState(3);
const [countPage, setCountPage] = useState(2);
const [compactPage, setCompactPage] = useState(5);
const [dotsPage, setDotsPage] = useState(3);
return (
<XDSStack direction="vertical" gap={5} hAlign="center" xstyle={styles.root}>
<XDSPagination
page={dotsPage}
onChange={setDotsPage}
totalPages={8}
variant="dots"
/>
<XDSPagination
page={compactPage}
onChange={setCompactPage}
totalPages={10}
variant="compact"
/>
<XDSPagination
page={countPage}
onChange={setCountPage}
totalItems={200}
pageSize={20}
variant="count"
/>
<XDSPagination
page={pagesPage}
onChange={setPagesPage}
totalItems={200}
pageSize={10}
variant="pages"
/>
</XDSStack>
);
}