# Pagination Pagination is used to navigate through a list of items. ## Import ```tsx import { Pagination } from '@coinbase/cds-web/pagination/Pagination' ``` ## Examples ### Basic Pagination A simple example showing basic pagination with 10 total pages. ```jsx live function BasicPaginationExample() { const [activePage, setActivePage] = useState(1); const totalPages = 10; return ; } ``` ### Pagination with First/Last Buttons Shows pagination with the optional First and Last page navigation buttons enabled. ```jsx live function FirstLastButtonsPaginationExample() { const [activePage, setActivePage] = useState(5); const totalPages = 15; return ( ); } ``` ### Pagination with Custom Counts Demonstrates using `siblingCount` and `boundaryCount` to adjust the number of pages displayed around the current page and at the boundaries, useful for larger page ranges. ```jsx live function CustomCountsPaginationExample() { const [activePage, setActivePage] = useState(10); const totalPages = 20; return ( ); } ``` ### Controlled Pagination This example explicitly manages the current page state, which might be necessary if the page change triggers other actions like fetching data. ```jsx live function ControlledPaginationExample() { const [activePage, setActivePage] = useState(3); const totalPages = 10; const handlePageChange = (newPage) => { console.log(`Navigating to page: ${newPage}`); // Here you might fetch data for the new page setActivePage(newPage); }; return ( ); } ``` ### Using the `usePagination` Hook This example shows how to use the headless `usePagination` hook to build a custom pagination interface. The hook provides the logic, and you render the UI. ```jsx live function UsePaginationHookExample() { const [activePage, setActivePage] = useState(5); const totalPages = 12; const { items, goNextPage, goPrevPage, updateActivePage, isFirstPage, isLastPage } = usePagination({ totalPages, activePage, onChange: setActivePage, siblingCount: 1, boundaryCount: 1, }); return ( {items.map((item, index) => { if (item.type === 'ellipsis') { // Render ellipsis as simple text return ( ); } // Render page buttons return ( ); })} ); } ``` ### Customized Components This example demonstrates how to customize the appearance of pagination by providing custom components for page buttons, navigation buttons, and ellipsis. ```jsx live function CustomizedPaginationExample() { const [activePage, setActivePage] = useState(5); const totalPages = 20; // Custom page button component const CustomPageButton = forwardRef( ({ page, isCurrentPage, onClick, accessibilityLabel }, ref) => ( onClick(page)} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick(page); } }} padding={1} role="button" tabIndex={0} style={{ cursor: 'pointer' }} > {page} ), ); // Custom navigation button component const CustomNavButton = forwardRef( ({ direction, disabled, onClick, accessibilityLabel }, ref) => { // Direction-specific arrows const getArrow = () => { switch (direction) { case 'first': return '««'; case 'previous': return '«'; case 'next': return '»'; case 'last': return '»»'; default: return ''; } }; return ( { if (!disabled && (e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); onClick(); } }} opacity={disabled ? 0.7 : 1} padding={1} role="button" tabIndex={disabled ? -1 : 0} style={{ cursor: disabled ? 'not-allowed' : 'pointer' }} > {getArrow()} ); }, ); // Custom ellipsis component const CustomEllipsis = ({ content = '•••', testID }) => ( ); return ( ); } ``` ### Pagination with Table This example demonstrates integrating the Pagination component with a Table, a common use case for pagination. ```jsx live function TablePaginationExample() { const totalResults = accounts.length; const PAGE_SIZE = 5; const [activePage, setActivePage] = useState(1); const [isFixed, setIsFixed] = useState(false); // Calculate pagination indexes const startIdx = (activePage - 1) * PAGE_SIZE; const endIdx = Math.min(startIdx + PAGE_SIZE, totalResults); const paginatedAccounts = accounts.slice(startIdx, endIdx); const totalPages = Math.ceil(totalResults / PAGE_SIZE); const toggleFixed = () => setIsFixed(!isFixed); return ( Fixed Layout Cryptocurrency Accounts {paginatedAccounts.map((account) => ( } title={account.name} subtitle={account.currency?.code} /> ))} Page {startIdx + 1}-{endIdx}
); } ``` ## Props | Prop | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `activePage` | `number` | Yes | `-` | React state for the currently active page. Current active page number (1-based) | | `onChange` | `(activePage: number) => void` | Yes | `-` | Callback that is fired when the active page changes. Use this callback to update the activePage state. | | `totalPages` | `number` | Yes | `-` | Total number of pages. | | `PaginationEllipsisComponent` | `PaginationEllipsisComponent` | No | `({ content = '...', testID }: PaginationEllipsisProps) => ( )` | Custom component for rendering ellipsis | | `PaginationNavigationButtonComponent` | `PaginationNavigationButtonComponent` | No | `forwardRef( ( { direction, onClick, disabled, accessibilityLabel, testID }: PaginationNavigationButtonProps, ref: React.ForwardedRef, ) => { return ( ); }, )` | Custom component for rendering navigation buttons. Must use forwardRef to properly receive and forward the ref to a focusable DOM element for focus management to work correctly. | | `PaginationPageButtonComponent` | `PaginationPageButtonComponent` | No | `forwardRef( ( { page, onClick, isCurrentPage, disabled, accessibilityLabel, testID, ...props }: PaginationPageButtonProps, ref: React.ForwardedRef, ) => { const handleClick = useCallback(() => onClick(page), [onClick, page]); const isSingleDigit = page < 10; return ( ); }, )` | Custom component for rendering page buttons. Must use forwardRef to properly receive and forward the ref to a focusable DOM element for focus management to work correctly. | | `accessibilityLabel` | `string` | No | `Pagination` | - | | `accessibilityLabels` | `{ next?: string; previous?: string \| undefined; first?: string \| undefined; last?: string \| undefined; page?: ((page: number) => string) \| undefined; } \| undefined` | No | `-` | Custom accessibility labels for navigation buttons | | `alignContent` | `ResponsiveProp
` | No | `-` | - | | `alignItems` | `ResponsiveProp
` | No | `-` | - | | `alignSelf` | `ResponsiveProp
` | No | `-` | - | | `as` | `div` | No | `-` | - | | `aspectRatio` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - | | `background` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | - | | `borderBottomLeftRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - | | `borderBottomRightRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - | | `borderBottomWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - | | `borderColor` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | - | | `borderEndWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - | | `borderRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - | | `borderStartWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - | | `borderTopLeftRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - | | `borderTopRightRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - | | `borderTopWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - | | `borderWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - | | `bordered` | `boolean` | No | `-` | Add a border around all sides of the box. | | `borderedBottom` | `boolean` | No | `-` | Add a border to the bottom side of the box. | | `borderedEnd` | `boolean` | No | `-` | Add a border to the trailing side of the box. | | `borderedHorizontal` | `boolean` | No | `-` | Add a border to the leading and trailing sides of the box. | | `borderedStart` | `boolean` | No | `-` | Add a border to the leading side of the box. | | `borderedTop` | `boolean` | No | `-` | Add a border to the top side of the box. | | `borderedVertical` | `boolean` | No | `-` | Add a border to the top and bottom sides of the box. | | `bottom` | `ResponsiveProp>` | No | `-` | - | | `boundaryCount` | `number` | No | `1` | Number of pages to show at the beginning and end of pagination. | | `color` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | - | | `columnGap` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - | | `dangerouslySetBackground` | `string` | No | `-` | - | | `disabled` | `boolean` | No | `-` | - | | `display` | `ResponsiveProp` | No | `-` | - | | `elevation` | `0 \| 1 \| 2` | No | `-` | - | | `flexBasis` | `ResponsiveProp>` | No | `-` | - | | `flexDirection` | `ResponsiveProp` | No | `-` | - | | `flexGrow` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset` | No | `-` | - | | `flexShrink` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset` | No | `-` | - | | `flexWrap` | `ResponsiveProp` | No | `-` | - | | `font` | `ResponsiveProp` | No | `-` | - | | `fontFamily` | `ResponsiveProp` | No | `-` | - | | `fontSize` | `ResponsiveProp` | No | `-` | - | | `fontWeight` | `ResponsiveProp` | No | `-` | - | | `gap` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - | | `grid` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| none` | No | `-` | - | | `gridArea` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - | | `gridAutoColumns` | `ResponsiveProp>` | No | `-` | - | | `gridAutoFlow` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| row \| column \| dense` | No | `-` | - | | `gridAutoRows` | `ResponsiveProp>` | No | `-` | - | | `gridColumn` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - | | `gridColumnEnd` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - | | `gridColumnStart` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - | | `gridRow` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - | | `gridRowEnd` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - | | `gridRowStart` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - | | `gridTemplate` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| none` | No | `-` | - | | `gridTemplateAreas` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| none` | No | `-` | - | | `gridTemplateColumns` | `ResponsiveProp>` | No | `-` | - | | `gridTemplateRows` | `ResponsiveProp>` | No | `-` | - | | `height` | `ResponsiveProp>` | No | `-` | - | | `justifyContent` | `ResponsiveProp` | No | `-` | - | | `key` | `Key \| null` | No | `-` | - | | `left` | `ResponsiveProp>` | No | `-` | - | | `lineHeight` | `ResponsiveProp` | No | `-` | - | | `margin` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - | | `marginBottom` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - | | `marginEnd` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - | | `marginStart` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - | | `marginTop` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - | | `marginX` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - | | `marginY` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - | | `maxHeight` | `ResponsiveProp>` | No | `-` | - | | `maxWidth` | `ResponsiveProp>` | No | `-` | - | | `minHeight` | `ResponsiveProp>` | No | `-` | - | | `minWidth` | `ResponsiveProp>` | No | `-` | - | | `opacity` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset` | No | `-` | - | | `overflow` | `ResponsiveProp` | No | `-` | - | | `padding` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - | | `paddingBottom` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - | | `paddingEnd` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - | | `paddingStart` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - | | `paddingTop` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - | | `paddingX` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - | | `paddingY` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - | | `pin` | `top \| bottom \| left \| right \| all` | No | `-` | Direction in which to absolutely pin the box. | | `position` | `ResponsiveProp` | No | `-` | - | | `ref` | `RefObject \| ((instance: HTMLDivElement \| null) => void) \| null` | No | `-` | - | | `right` | `ResponsiveProp>` | No | `-` | - | | `rowGap` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - | | `showFirstLastButtons` | `boolean` | No | `-` | Whether to show first and last page navigation buttons | | `siblingCount` | `number` | No | `1` | Number of pages to show on each side of current page. | | `style` | `CSSProperties` | No | `-` | - | | `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID | | `testIDMap` | `{ nav?: string; nextButton?: string \| undefined; prevButton?: string \| undefined; firstButton?: string \| undefined; lastButton?: string \| undefined; } \| undefined` | No | `-` | Custom test IDs for specific elements within pagination | | `textAlign` | `ResponsiveProp
` | No | `-` | - | | `textDecoration` | `ResponsiveProp` | No | `-` | - | | `textTransform` | `ResponsiveProp` | No | `-` | - | | `top` | `ResponsiveProp>` | No | `-` | - | | `transform` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| none` | No | `-` | - | | `userSelect` | `ResponsiveProp` | No | `-` | - | | `visibility` | `ResponsiveProp` | No | `-` | - | | `width` | `ResponsiveProp>` | No | `-` | - | | `zIndex` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - |