Skip to main content

rc-pagination

Pagination enhancer for a native <nav> holding page links or buttons, with a server-safe page range helper.

Package
@rcarls/rc-pagination
Element
<rc-pagination>
Native dependency
Requires a native nav child with data-rc-page links or buttons
State model
Controlled or uncontrolled page
Main events
rc-pagination-change

Installation​

npm install @rcarls/rc-webcomponents@next
import '@rcarls/rc-webcomponents/rc-pagination/define';

Usage​

There is no WAI-ARIA pattern for pagination. The markup follows the GOV.UK and USWDS models: a <nav> landmark holding a list, so screen readers announce how many items it has. Tag each control with data-rc-page, a page number or previous, next, first, or last:

<rc-pagination total="12" page="6">
<nav>
<ul>
<li><a href="?page=5" data-rc-page="previous" rel="prev">Previous</a></li>
<li><a href="?page=1" data-rc-page="1">1</a></li>
<li aria-hidden="true">…</li>
<li><a href="?page=5" data-rc-page="5">5</a></li>
<li><a href="?page=6" data-rc-page="6" aria-current="page">6</a></li>
<li><a href="?page=7" data-rc-page="7">7</a></li>
<li aria-hidden="true">…</li>
<li><a href="?page=12" data-rc-page="12">12</a></li>
<li><a href="?page=7" data-rc-page="next" rel="next">Next</a></li>
</ul>
</nav>
</rc-pagination>

Links make the markup work with no JavaScript at all. Use buttons for paging that only happens on the client.

Building the list​

paginationRange() returns the page numbers to render, with 'ellipsis' where a run of pages is hidden. It has no DOM dependency, so a server component or route can import it from @rcarls/rc-pagination/range without loading the element:

import { paginationRange } from '@rcarls/rc-pagination/range';

paginationRange({ page: 6, total: 12 }); // [1, 'ellipsis', 5, 6, 7, 'ellipsis', 12]
paginationRange({ page: 6, total: 12, siblings: 2, boundaries: 2 });

Live demo​

Behavior​

  • The element keeps aria-current="page" on the control for the current page.
  • It names the <nav> "Pagination" when it has no name, and gives numbered controls the name "Page 3", which keeps the visible number in the name. Previous, next, first, and last controls keep their visible text; icon-only ones are named "Previous page" and so on. An authored aria-label always wins.
  • Steps past either end are disabled: buttons natively, links with aria-disabled="true" and a guard that ignores their clicks. An authored disabled state remains when a control leaves the edge.
  • Activating a control fires rc-pagination-change with { page, previousPage }. In uncontrolled use the element moves to the requested page; with page set, the host decides by writing page. A numbered target beyond total requests the last page, and a stale link to that target does not navigate to its out-of-range URL.
  • rc-pagination-change is cancelable. Canceling it also cancels the click, so a link does not navigate: use this to hand the change to a client-side router.

Accessibility​

  • Give each pagination landmark a distinct name when a page has more than one, such as aria-label="Search results pages".
  • After a client-side page change, move focus to the start of the new results, or set announce to have the element announce "Page 6 of 12" through a polite live region.
  • Hide the ellipsis items from assistive technology with aria-hidden="true"; the page names already convey the position.

Theming​

The default demo mode shows the component without a package theme. Use the shared preview controls to compare themes.

API​

Properties​

PropertyMarkupTypeDefaultDescription
totaltotalnumber1Number of pages.
announceannouncebooleanfalseAnnounces each user change through a polite live region.
messagesJS property onlyPartial<RCPaginationMessages> | undefinedNot specifiedBuilt-in text overrides; unset keys fall back to registered text, then English.
pagepagenumberNot specifiedCurrent page, 1-based and clamped to `[1, total]`. Host writes are silent.
defaultPagedefault-pagenumberNot specifiedInitial page for uncontrolled use.

Events​

EventDetail typeDescription
rc-pagination-changeNo detail type documentedFired when the user activates a page control. Cancelable; canceling it also cancels the click, so a link does not navigate.

CSS Custom Properties​

PropertyDefaultDescription
--rc-pagination-gap0.25emSpace between items.
--rc-pagination-item-min-inline-sizeNot specifiedMinimum inline size of each item. Defers to the content size when unset.