Skip to content

Pagination

NavigationSince 0.2.0
import { Pagination, getPageItems } from '@absoluteui/components/pagination';
import '@absoluteui/components/pagination/style.css';

Using Angular? See Pagination for Angular.

Navigate paged data. Pagination renders a labelled <nav> of native buttons: previous/next, optional first/last, and page numbers with ellipses. It can also show a localized result summary (“Showing 11-20 of 57”) and a native “Rows per page” <select>. User page changes are announced politely. In narrow containers the page numbers give way to “Page 3 of 10”. The pure helper getPageItems computes which pages to show, for custom renderers.

  • Tables and result lists split into pages, where users need to know and choose their position.
  • Server-paged data where the total or page count is known.
  • Feeds consumed sequentially: use ‘Load more’ or infinite scrolling with a clear end.
  • Fewer items than one page: render the list without pagination.
  • Step-by-step processes: use a stepper.
import { Pagination, type PaginationChangeDetail } from '@absoluteui/components/pagination';
const host = document.createElement('div');
document.body.append(host);
const load = (page: number, pageSize: number | null): void => {
host.dataset.request = `${String(page)}:${String(pageSize)}`;
};
const pager = Pagination.create(host, { total: 57, pageSize: 10, pageSizes: [10, 25, 50] });
host.addEventListener('aui-change', (event) => {
const { page, pageSize } = (event as CustomEvent<PaginationChangeDetail>).detail;
load(page, pageSize);
});
pager.update({ total: 64 });
import { Pagination } from '@absoluteui/components/pagination';
const host = document.createElement('div');
document.body.append(host);
const pager = Pagination.create(host, { pageCount: 40, page: 12, showFirstLast: true, siblingCount: 2 });
pager.setPage(20);
import { getPageItems } from '@absoluteui/components/pagination';
const items = getPageItems(5, 10);
const text = items.map((item) => (item === 'ellipsis' ? '…' : String(item))).join(' ');
document.title = text;
import { Pagination, type PaginationChangeDetail } from '@absoluteui/components/pagination';
import '@absoluteui/components/pagination/style.css';
const host = document.querySelector<HTMLElement>('#orders-pager');
if (host) {
const pager = Pagination.create(host, { total: 240, pageSize: 25, pageSizes: [25, 50, 100] });
host.addEventListener('aui-change', (event) => {
const { page, pageSize } = (event as CustomEvent<PaginationChangeDetail>).detail;
host.dataset.query = `page=${String(page)}&size=${String(pageSize)}`;
pager.update({ total: 240 });
});
}
import { Pagination } from '@absoluteui/components/pagination';
const host = document.createElement('div');
document.body.append(host);
Pagination.create(host, { total: 57, pageSize: 10, page: 2 });
<div class="aui-pagination"> (consumer host, size container)
<nav class="aui-pagination__nav" aria-label="Pagination">
<p class="aui-pagination__summary">Showing 11-20 of 57</p> (total known)
<div class="aui-pagination__controls">
<button class="aui-pagination__button aui-pagination__first" aria-label="First page"> (showFirstLast)
<button class="aui-pagination__button aui-pagination__previous" aria-label="Previous page">
<ul class="aui-pagination__pages">
<li class="aui-pagination__item"><button class="aui-pagination__page" aria-label="Page 1">1</button></li>
<li class="aui-pagination__item aui-pagination__ellipsis" aria-hidden="true">...</li>
<li class="aui-pagination__item"><button class="aui-pagination__page" aria-current="page" aria-label="Page 3">3</button></li>
</ul>
<span class="aui-pagination__compact">Page 3 of 10</span> (narrow containers only)
<button class="aui-pagination__button aui-pagination__next" aria-label="Next page">
<button class="aui-pagination__button aui-pagination__last" aria-label="Last page"> (showFirstLast)
</div>
<div class="aui-pagination__size"> (pageSizes)
<label class="aui-pagination__size-label" for>Rows per page</label>
<select class="aui-pagination__size-select">
</div>
</nav>
</div>
Pagination.create(host: HTMLElement, options: PaginationOptions): Pagination

Global configuration key: pagination (see Configuration).

Option Type Default Description
page number 1 Current page, 1-based; clamped to 1..pageCount.
pageCount number Number of pages. Provide either pageCount or total.
total number Total items; the page count is derived from pageSize. Enables the summary.
pageSize number Items per page. Required with total unless pageSizes is set (then defaults to its first entry).
pageSizes readonly number[] Render a native ‘Rows per page’ select with these sizes.
label string "Pagination (localized)" Accessible name of the <nav>.
icons Partial<{ previous: IconRef; next: IconRef; first: IconRef; last: IconRef }> Icon slot overrides.
siblingCount number 1 Pages shown on each side of the current page (0 to 10). Globally configurable.
boundaryCount number 1 Pages always shown at the start and end (0 to 10). Globally configurable.
showFirstLast boolean false Render first/last page buttons. Globally configurable.
showSummary boolean true Show ‘Showing 11-20 of 57’ when total is known. Globally configurable.
locale string "en-US" Message and number-formatting locale. Globally configurable. Inherited from common configuration.
direction 'ltr' | 'rtl' "ltr" rtl mirrors the direction icons. Globally configurable. Inherited from common configuration.
Property Type Description
host HTMLElement Consumer host; its children are owned by the component.
nav HTMLElement The <nav> landmark.
page number Current page (1 when there are no pages).
pageCount number Number of pages (given or derived from total and pageSize).
pageSize number | null Items per page, or null when only pageCount is known.
options Readonly<PaginationOptions> Current instance options.
Method Description
setPage(page: number): void Go to a page programmatically (clamped; no events, no announcement).
update(options: Partial<PaginationOptions>): void Merge and apply options. Passing pageCount drops total and vice versa.
destroy(): void Remove rendering, listeners, and subscriptions.
getPageItems(page: number, pageCount: number, siblingCount?: number, boundaryCount?: number): (number | 'ellipsis')[] Pure helper (module export): the page numbers and ellipses to render.
Event Detail Bubbles Cancelable Description
aui-change { page: number; pageSize: number | null } Yes No The user changed the page or the page size.

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-pagination Host (size container).
aui-pagination--rtl Configured rtl direction.
aui-pagination__nav The <nav> landmark.
aui-pagination__summary Result summary.
aui-pagination__controls Buttons row.
aui-pagination__button Any pagination button.
aui-pagination__first | __previous | __next | __last Direction buttons.
aui-pagination__pages List of page items.
aui-pagination__item A page item <li>.
aui-pagination__page A page button; [aria-current=page] for the current one.
aui-pagination__ellipsis Decorative gap marker.
aui-pagination__compact ‘Page 3 of 10’ text shown in narrow containers.
aui-pagination__size | __size-label | __size-select Rows-per-page field.
Slot Default icon
previous chevron-left
next chevron-right
first chevrons-left
last chevrons-right

Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.

--aui-border-width | --aui-color-border-strong | --aui-color-focus-ring | --aui-color-hover | --aui-color-primary | --aui-color-selected | --aui-color-selected-text | --aui-color-surface | --aui-color-text | --aui-color-text-disabled | --aui-color-text-muted | --aui-control-height-md | --aui-control-padding-x | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-md | --aui-font-size-sm | --aui-font-weight-semibold | --aui-icon-size-sm | --aui-line-height-normal | --aui-radius-md | --aui-space-1 | --aui-space-2 | --aui-space-3 | --aui-touch-target-min

type PageItem = number | 'ellipsis';
function getPageItems(page: number, pageCount: number, siblingCount?: number, boundaryCount?: number): PageItem[];
interface PaginationIcons { previous: IconRef; next: IconRef; first: IconRef; last: IconRef }
interface PaginationChangeDetail { page: number; pageSize: number | null }

Pattern: Navigation landmark of native buttons (no dedicated APG pattern), following common accessible pagination practice: named buttons, aria-current on the current page, disabled bounds, and announced changes.

Key Context Action
Tab / Shift+Tab Any Moves between enabled buttons and the rows-per-page select (native tab order).
Enter / Space Button focused Activates the button (changes the page).
Arrow keys Rows-per-page select focused Native select behavior.
Element Role
<nav> navigation (native landmark)
<ul> list (native) of page items
<button> button (native)
<select> combobox (native select)
Attribute Element Value
aria-label <nav> label option or localized ‘Pagination’
aria-label page button Localized ‘Page 3’ with a locale-formatted number
aria-current current page button page
aria-label first/previous/next/last Localized ‘First page’, ‘Previous page’, ‘Next page’, ‘Last page’
aria-hidden ellipsis items, icons true (decorative)
disabled previous/next/first/last Native disabled at the bounds

After a page change the page buttons are re-rendered; focus stays on the same control (for example Next, or the page number clicked). When that control becomes disabled or hidden, focus moves to the current page button, then to previous/next.

Criterion Level Note
1.3.1 Info and Relationships A
1.4.1 Use of Color A
1.4.10 Reflow AA
2.1.1 Keyboard A
2.4.3 Focus Order A
2.4.7 Focus Visible AA
4.1.2 Name, Role, Value A
4.1.3 Status Messages AA

Automated test results: Accessibility and compliance.

  • User page changes are announced through the shared polite live region: the summary (‘Showing 11-20 of 57’) when the total is known, otherwise ‘Page 3 of 10’. Programmatic changes are not announced.
  • The rows-per-page select has a visible native label.
  • Manual screen-reader verification (NVDA, JAWS, VoiceOver) is pending.

default, hover, focus-visible, current page, disabled (at the bounds), compact (narrow container), empty (no pages).

From Trigger To
page n page button, previous, next, first, or last new page; aui-change; announcement
page n, size s rows-per-page select size s′; page recomputed so the first item of the old page stays visible; aui-change
any setPage(n) / update({ page }) page clamped to 1..pageCount; no event
page n update({ total }) makes the count smaller page clamped; no event
  • User page changes are announced through the shared polite live region: the summary when the total is known, otherwise “Page 3 of 10”. Programmatic changes are silent.
  • Focus stays on the activated control across re-renders. If that control becomes disabled (for example Previous on page 1), focus moves to the current page button.
  • The current page is shown by fill, border, and weight, not by color alone.
  • Manual screen-reader verification is pending (see Known limitations).

The host is a size container. Below 30rem of inline space, page numbers are hidden and “Page 3 of 10” is shown between previous and next. Buttons keep the minimum touch-target size. Summary, controls, and the size select wrap as needed.

Layout follows the document direction. Chevron icons are mirrored when direction is 'rtl' or when the element is in an rtl context (:dir(rtl)), so “previous” always points against the reading direction.

Colors come from role tokens only. Verified in the light, dark, and high-contrast themes. In forced-colors mode the current page uses Highlight/HighlightText and disabled buttons use GrayText.

configureAbsoluteUI({ components: { pagination: { siblingCount: 2, showFirstLast: true } } });

Changes apply to existing instances immediately.

Instance options override global and theme defaults. All options are dynamic.

icons.previous (default chevron-left), icons.next (chevron-right), icons.first (chevrons-left), and icons.last (chevrons-right).

All text uses textContent; nothing is parsed as HTML. CSP-safe.

No persistence, network access, or telemetry. The component never fetches data; load pages on aui-change.

Two listeners (a delegated click on the controls, and change on the select) plus one configuration subscription per instance. Rendered page items are bounded by siblingCount and boundaryCount, not by the page count. Budget: under 1 ms for Pagination.create with 10,000 pages.

Any page count up to Number.MAX_SAFE_INTEGER. getPageItems is tested exhaustively for page counts 0 to 30 across seven sibling/boundary combinations.

  • total: 0 gives zero pages: no page buttons, previous and next disabled, and the summary “No results”.
  • One page: a single current page button; previous and next disabled.
  • page outside 1..pageCount is clamped.
  • Changing rows per page from 10 to 25 on page 4 (items 31-40) goes to page 2 (items 26-50).
  • boundaryCount: 0 does not always show the first and last pages; combine it with showFirstLast.
Code Category Cause
PAGINATION_OPTIONS configuration Neither or both of pageCount/total; negative or non-integer counts; total without pageSize/pageSizes; a pageSize not in pageSizes; empty pageSizes; an empty label; a non-integer page.
PAGINATION_ITEMS configuration getPageItems called with non-integer or negative arguments.
CFG_INVALID_VALUE configuration Invalid siblingCount, boundaryCount, showFirstLast, showSummary, locale, or direction.

A failed construction leaves no rendering, listeners, or subscriptions behind.

  • No “go to page” input.
  • The compact breakpoint (30rem container width) is fixed.
  • Screen-reader behavior has not yet been manually verified with NVDA, JAWS, and VoiceOver.
  • Cursor-based pagination without a known page count.
  • Page items as links (href).