Skip to contentSkip to navigationSkip to topbar
Figma
Star

Pagination

Version 7.1.1GithubStorybook

Pagination lets users navigate through content or a dataset that’s been broken up into multiple pages.

Component preview theme
<Pagination label="truncated pagination navigation">
<PaginationItems>
<PaginationArrow label="Go to previous page" variant="back" />
<PaginationNumbers>
<PaginationNumber label="Go to page 1">1</PaginationNumber>
<PaginationEllipsis label="Collapsed previous pages" />
<PaginationNumber label="Go to page 4">4</PaginationNumber>
<PaginationNumber label="Go to page 5" isCurrent>5</PaginationNumber>
<PaginationNumber label="Go to page 6">6</PaginationNumber>
<PaginationEllipsis label="Collapsed next pages" />
<PaginationNumber label="Go to page 9">9</PaginationNumber>
</PaginationNumbers>
<PaginationArrow label="Go to next page" variant="forward" />
</PaginationItems>
</Pagination>

Guidelines

Guidelines page anchor

About Pagination

About Pagination page anchor

Use Pagination to split up content when a user’s goal is "to find a specific item [in a list] and click through to that destination page" (source: Nielsen Norman Group(link takes you to an external page)). Pagination is used to communicate only where a user is in paged content. It isn’t used to show the status of each page—use a Progress Stepper (coming soon) instead.

Pagination is often paired with Tables, one of the most commonly used components in Twilio and one of the primary ways customers view content in our products. Think of Pagination as an important navigation element that helps our customers understand exactly where they are and help them decide where to go next.

The component is made up of 6 main parts:

  • Pagination: The outer pagination container with the accessible role=navigation
  • PaginationItems: A wrapper that displays the pagination contents inline.
  • PaginationArrow: Left and right arrow controls, with an option to add text labels
  • PaginationLabel: Indicates the current page in view (e.g., "Page 2", "1–10 of 50 results")
  • PaginationNumbers: A wrapper for the list of page numbers and the total page count
  • PaginationNumber: Page number controls

Accessibility Information

Accessibility Information page anchor
  • The Pagination component supports navigation by the use of either a button or anchor. Use Pagination as an anchor if the URL changes for each page. Use it as a button if the URL doesn’t change for each page. Check out our documentation on when to use a Button vs. an Anchor.
  • Use the Pagination component when it controls a large part of the page in view, like a full-width Table. Since Pagination typically appears underneath the UI element it’s controlling, it needs to be obvious that it is controlling the element above it, rather than anything else below it. For example, if you need to let users page through a small table in a card on a dashboard page, consider letting them expand the table and show Pagination on the expanded view instead.

Use the default Pagination component for paged datasets like event logs, where the dataset is constantly increasing in size, or where the total number of pages is unknown.

At minimum, the default Pagination component shows the Pagination arrows. However whenever possible, show the current page number too, so that users know where they are in a dataset and get confirmation that they’ve moved to a new page.

Component preview theme
const DefaultPagination = () => {
const [currentPage, setCurrentPage] = React.useState(1);
const totalPages = 5;
const goToNextPage = (event) => {
setCurrentPage((page) => Math.min(page + 1, totalPages));
event.preventDefault();
};
const goToPreviousPage = (event) => {
setCurrentPage((page) => Math.max(page - 1, 1));
event.preventDefault();
};
return (
<Pagination label="default pagination navigation">
<PaginationItems>
<PaginationArrow label="Go to previous page" variant="back" onClick={goToPreviousPage} disabled={currentPage === 1} />
<PaginationLabel>
Page {currentPage}
</PaginationLabel>
<PaginationArrow label="Go to next page" variant="forward" onClick={goToNextPage} disabled={currentPage === 5} />
</PaginationItems>
</Pagination>
);
};
render(
<DefaultPagination />
)

Default pagination as anchor

Default pagination as anchor page anchor

Use Pagination as an anchor if the URL changes for each page. Both the PaginationArrow and PaginationNumber components can be set as anchors using the as="a" prop and including an href prop.

Component preview theme
<Stack orientation="vertical" spacing="space50">
<Pagination label="labelled anchor pagination navigation">
<PaginationItems>
<PaginationArrow as="a" href="#" label="Go to previous page" variant="back" />
<PaginationLabel>Page 2</PaginationLabel>
<PaginationArrow as="a" href="#" label="Go to next page" variant="forward" />
</PaginationItems>
</Pagination>
<Pagination label="anchor pagination navigation">
<PaginationItems>
<PaginationArrow as="a" href="#" label="Go to previous page" variant="back" />
<PaginationArrow as="a" href="#" label="Go to next page" variant="forward" />
</PaginationItems>
</Pagination>
</Stack>

Default pagination as button

Default pagination as button page anchor

Use Pagination as a button if the URL doesn’t change for each page. Both the PaginationArrow and PaginationNumber components are buttons by default.

Component preview theme
<Stack orientation="vertical" spacing="space50">
<Pagination label="labelled button pagination navigation">
<PaginationItems>
<PaginationArrow label="Go to previous page" variant="back" />
<PaginationLabel>Page 2</PaginationLabel>
<PaginationArrow label="Go to next page" variant="forward" />
</PaginationItems>
</Pagination>
<Pagination label="button pagination navigation">
<PaginationItems>
<PaginationArrow label="Go to previous page" variant="back" />
<PaginationArrow label="Go to next page" variant="forward" />
</PaginationItems>
</Pagination>
</Stack>

Pagination with custom labels

Pagination with custom labels page anchor

Adding labels to Pagination can help give customers more information about what kind of data is in view and in what direction the pages are moving. For example, when a dataset is sorted chronologically, it might not be immediately clear whether the "Next" button takes you forward or backward in time.

Component preview theme
<>
<Pagination label="custom label pagination navigation">
<PaginationItems>
<PaginationArrow label="Go to previous month" variant="back" visibleLabel="Previous" />
<PaginationLabel>February 2021</PaginationLabel>
<PaginationArrow label="Go to next month" variant="forward" visibleLabel="Next" />
</PaginationItems>
</Pagination>
<Text as="span" display="block" marginTop="space90" color="colorTextWeak">Will pressing the "Next" button take you to January or March?</Text>
</>

To give customers further clarity on where they are in a data set, you can add custom text to:

  • The current page label
  • The left and right arrow controls
Component preview theme
<Pagination label="current page label pagination navigation">
<PaginationItems>
<PaginationArrow label="Go to newer templates" variant="back" visibleLabel="Newer templates" />
<PaginationLabel>51–100 of 500+ templates</PaginationLabel>
<PaginationArrow label="Go to older templates" variant="forward" visibleLabel="Older templates" />
</PaginationItems>
</Pagination>

Pagination with total page count

Pagination with total page count page anchor

Use the Pagination variant with total page count in cases when knowing the exact size of a dataset is crucial to a customer’s understanding of the content (e.g., knowing how many phone numbers aren’t compliant) and when you’re able to fetch the data in a performant way.

This variant also allows customers to skip ahead pages in a dataset, though this hasn't been identified as a common use case.

(information)

Considering the tradeoffs for showing the total count of a dataset

By default, Twilio APIs might not support showing current page number or the total count of a dataset(link takes you to an external page).

However, anecdotal evidence from product designers suggests that knowing the size of a dataset is important to Twilio customers in certain cases. Showing the total count of a dataset makes sense especially when the customer has more active control over total count, such as with total SIMs, but not for datasets that rapidly increase, like event logs. If the exact total count is unknown, consider indicating size in another way that gives customers an approximate idea of the size of a dataset, like showing "1–10 of 100+ results".

Component preview theme
const NumberPagination = () => {
const [currentPage, setCurrentPage] = React.useState(1);
const pages = [1, 2, 3, 4, 5];
const totalPages = pages.length;
const goToNextPage = (event) => {
setCurrentPage((page) => Math.min(page + 1, totalPages));
event.preventDefault();
};
const goToPreviousPage = (event) => {
setCurrentPage((page) => Math.max(page - 1, 1));
event.preventDefault();
};
const goToPage = (event) => {
setCurrentPage(parseInt(event.target.innerText));
event.preventDefault();
};
return (
<>
<Table>
<THead>
<Tr>
<Th>Date</Th>
<Th textAlign="right">Message count</Th>
</Tr>
</THead>
<TBody>
<Tr>
<Td>Oct 21, 2020</Td>
<Td textAlign="right">3</Td>
</Tr>
<Tr>
<Td>Oct 20, 2020</Td>
<Td textAlign="right">6</Td>
</Tr>
<Tr>
<Td>Oct 19, 2020</Td>
<Td textAlign="right">13</Td>
</Tr>
<Tr>
<Td>Oct 18, 2020</Td>
<Td textAlign="right">9</Td>
</Tr>
</TBody>
</Table>
<Box display="flex" justifyContent="center" marginTop="space70">
<Pagination label="numbered pagination navigation">
<PaginationItems>
<PaginationArrow label="Go to previous page" variant="back" onClick={goToPreviousPage} disabled={currentPage === 1} />
<PaginationNumbers>
{pages.map((page, index) => {
const goToPageString = 'Go to page ';
return (
<PaginationNumber label={goToPageString + page} isCurrent={page === currentPage} onClick={goToPage} key={index}>
{page}
</PaginationNumber>
);
})}
</PaginationNumbers>
<PaginationArrow label="Go to next page" variant="forward" onClick={goToNextPage} disabled={currentPage === 5}/>
</PaginationItems>
</Pagination>
</Box>
</>
);
};
render(
<NumberPagination />
)

Make sure the current page label and arrow controls in the Pagination component indicate to the user which page of results they are on and which way they'll go in the data set when using the arrows. Show the current page number whenever possible.

When including custom labels, use sentence case and keep labels under 30 characters.

The Pagination component should be centered underneath the content it controls separated by a margin of $space-70.

Component preview theme
const PositioningPagination = () => {
const [currentPage, setCurrentPage] = React.useState(1);
const pages = [1, 2, 3, 4];
const totalPages = pages.length;
const results = ['1–5 of 20 results', '5-10 of 20 results', '10-15 of 20 results', '15-20 of 20 results'];
const goToNextPage = (event) => {
setCurrentPage((page) => Math.min(page + 1, totalPages));
event.preventDefault();
};
const goToPreviousPage = (event) => {
setCurrentPage((page) => Math.max(page - 1, 1));
event.preventDefault();
};
const goToPage = (event) => {
setCurrentPage(parseInt(event.target.innerText));
event.preventDefault();
};
return (
<>
<Table>
<THead>
<Tr>
<Th>Date</Th>
<Th>SID</Th>
<Th>From</Th>
</Tr>
</THead>
<TBody>
<Tr>
<Td>
<Text as="p">16:24:28 PDT</Text>
<Text as="p">2020-09-17</Text>
</Td>
<Td>
<Text as="span" fontFamily="fontFamilyCode">
SM0yc4mxi6cn4z13bte7qmflc2drc85mlp
</Text>
</Td>
<Td>(602) 609-6747</Td>
</Tr>
<Tr>
<Td>
<Text as="p">16:24:28 PDT</Text>
<Text as="p">2020-09-17</Text>
</Td>
<Td>
<Text as="span" fontFamily="fontFamilyCode">
SMl29llgoihx286uhxfb0yc5n0sg391x5n
</Text>
</Td>
<Td>(602) 609-6747</Td>
</Tr>
<Tr>
<Td>
<Text as="p">16:24:28 PDT</Text>
<Text as="p">2020-09-17</Text>
</Td>
<Td>
<Text as="span" fontFamily="fontFamilyCode">
SMxarke3v30fv17hauqn86a7nhgm3b5d87
</Text>
</Td>
<Td>(602) 609-6747</Td>
</Tr>
<Tr>
<Td>
<Text as="p">16:24:28 PDT</Text>
<Text as="p">2020-09-17</Text>
</Td>
<Td>
<Text as="span" fontFamily="fontFamilyCode">
SM0yc4mxi6cn4z13bte7qmflc2drc85mlp
</Text>
</Td>
<Td>(602) 609-6747</Td>
</Tr>
<Tr>
<Td>
<Text as="p">16:24:28 PDT</Text>
<Text as="p">2020-09-17</Text>
</Td>
<Td>
<Text as="span" fontFamily="fontFamilyCode">
SMl29llgoihx286uhxfb0yc5n0sg391x5n
</Text>
</Td>
<Td>(602) 609-6747</Td>
</Tr>
</TBody>
</Table>
<Box display="flex" justifyContent="center" marginTop="space70">
<Pagination label="paged pagination navigation">
<PaginationItems>
<PaginationArrow label="Go to previous page" variant="back" onClick={goToPreviousPage} disabled={currentPage === 1} />
<PaginationNumbers pageLabel={results[currentPage - 1]}>
{pages.map((page, index) => {
const goToPageString = 'Go to page ';
return (
<PaginationNumber label={goToPageString + page} isCurrent={page === currentPage} onClick={goToPage} key={index}>
{page}
</PaginationNumber>
);
})}
</PaginationNumbers>
<PaginationArrow label="Go to next page" variant="forward" onClick={goToNextPage} disabled={currentPage === 4} />
</PaginationItems>
</Pagination>
</Box>
</>
);
};
render(
<PositioningPagination />
)

Using PaginationEllipsis

Using PaginationEllipsis page anchor

When the number of pages exceeds 7, PaginationEllipsis can be used to indicate there are remaining pages.

Component preview theme
<Stack orientation="vertical" spacing="space50">
<Pagination label="truncated pagination navigation">
<PaginationItems>
<PaginationArrow label="Go to previous page" variant="back" />
<PaginationNumbers>
<PaginationNumber label="Go to page 1">1</PaginationNumber>
<PaginationEllipsis label="Collapsed previous pages" />
<PaginationNumber label="Go to page 4">4</PaginationNumber>
<PaginationNumber label="Go to page 5" isCurrent>5</PaginationNumber>
<PaginationNumber label="Go to page 6">6</PaginationNumber>
<PaginationNumber label="Go to page 7">7</PaginationNumber>
<PaginationNumber label="Go to page 8">8</PaginationNumber>
</PaginationNumbers>
<PaginationArrow label="Go to next page" variant="forward" />
</PaginationItems>
</Pagination>
<Pagination label="truncated pagination navigation">
<PaginationItems>
<PaginationArrow label="Go to previous page" variant="back" />
<PaginationNumbers>
<PaginationNumber label="Go to page 1">1</PaginationNumber>
<PaginationNumber label="Go to page 2">2</PaginationNumber>
<PaginationNumber label="Go to page 3">3</PaginationNumber>
<PaginationNumber label="Go to page 4" isCurrent>4</PaginationNumber>
<PaginationNumber label="Go to page 5">5</PaginationNumber>
<PaginationEllipsis label="Collapsed next pages" />
<PaginationNumber label="Go to page 8">8</PaginationNumber>
</PaginationNumbers>
<PaginationArrow label="Go to next page" variant="forward" />
</PaginationItems>
</Pagination>
</Stack>

Double PaginationEllipsis is used when the current page is more than 3 pages from the first or last page.

Component preview theme
<Pagination label="truncated pagination navigation">
<PaginationItems>
<PaginationArrow label="Go to previous page" variant="back" />
<PaginationNumbers>
<PaginationNumber label="Go to page 1">1</PaginationNumber>
<PaginationEllipsis label="Collapsed previous pages" />
<PaginationNumber label="Go to page 4">4</PaginationNumber>
<PaginationNumber label="Go to page 5" isCurrent>5</PaginationNumber>
<PaginationNumber label="Go to page 6">6</PaginationNumber>
<PaginationEllipsis label="Collapsed next pages" />
<PaginationNumber label="Go to page 9">9</PaginationNumber>
</PaginationNumbers>
<PaginationArrow label="Go to next page" variant="forward" />
</PaginationItems>
</Pagination>

Paging through chronological data

Paging through chronological data page anchor

Add custom text to the arrow controls (e.g., "Older"/"Newer", "Previous month"/"next month") for datasets sorted by time or date. This makes it clear what direction the pages are moving.

If a customer can change the sort order of the dataset, make sure you swap the labelling of the arrow controls, as well.

Determining how many items to show per page

Determining how many items to show per page page anchor

The default number of items in view for paged content should generally scale up based on how important the content is to the customer’s goals, and scale down with the complexity or visual size of the dataset. For example, you might want to show fewer items per page for pages of Cards, or for Tables with tall rows.

Most Twilio datasets show 10 or 50 rows per page by default. However, a generic rule doesn't always work for complex, context-dependent data. We recommend using research to reach a sensible default and solution that works best for the customer.

If a single default can’t satisfy customers’ needs, you can give customers a way to change the number of items in view. Try to keep these options limited to reduce cognitive overhead, especially if the choice isn’t critical to the customer’s main goal.

Many sites let users choose how many items they’ll see on each page. This is often overkill...It’s usually better to offer a single default number — such as 10 or 20 — [or] give users the choice between two numbers, say 10 and 50, where the second number is substantially bigger than the default. If the choice is between two relatively similar numbers (such as 10 and 20), users might as well click the Next Page button rather than suffer the cognitive overhead of trying to decide their display preference.

Consider contributing a Table Actions pattern to help standardize this across Twilio!

Do

Use Pagination to let users page through items where a user is trying to find a specific item.

Don't

Don’t use the Pagination component to help users navigate through linear multi-step content like paged forms. In these cases, use a Progress Stepper (coming soon) or something that can communicate more about a user’s status through a flow than the Pagination component allows.

Do

Add custom, context-specific information to page labels and left and right page controls to give customers more clarity about where they are and where they’re going, when needed.

Don't

Don’t over-complicate Pagination labels with too much information about the content in view. If you can’t give succinct labels to Pagination, consider showing the information elsewhere on the page.

Do

Show current page count whenever possible so users understand where they are in a dataset, and so that they know the content in view has been updated if they navigate to a new page number.