Navigation
Pagination
Controls for navigating through pages of paginated content.
Last verified against WCAG 2.2 and WAI-ARIA APG 1.2 on 2026-07-02.
Implementation
pagination-pattern.tsx
function PaginationPattern() {
const [page, setPage] = useState(4);
const pages = getVisiblePages(page, TOTAL_PAGES);
return (
<nav aria-label="Pagination">
<button disabled={page === 1} onClick={() => setPage(p => p - 1)}>
Previous
</button>
{pages.map((entry, i) =>
entry === "ellipsis" ? (
<span key={i} aria-hidden="true">…</span> // decorative, not a control
) : (
<button
key={entry}
aria-current={entry === page ? "page" : undefined}
onClick={() => setPage(entry)}
>
{entry}
</button>
)
)}
<button disabled={page === TOTAL_PAGES} onClick={() => setPage(p => p + 1)}>
Next
</button>
</nav>
);
}Live demo
Required roles, states & properties
| Element | Attribute | Why |
|---|---|---|
| Wrapping element | <nav aria-label="Pagination"> | Creates a labeled navigation landmark so AT users can jump straight to the pagination controls, distinguishing it from other <nav> landmarks on the page (e.g. primary site nav). |
| Current page control | aria-current="page" | Identifies which page is currently displayed so AT announces "current page" — required in addition to any visual highlight, not instead of it. |
| Previous / Next controls | disabled attribute (real, not just styled) | A genuinely disabled button is skipped in the tab order and announced as unavailable, rather than remaining clickable while merely looking greyed out. |
| Ellipsis (…) | aria-hidden="true", non-interactive element | The truncation marker conveys no operable function, so it must not be a button or a Tab stop — it's purely decorative and should be hidden from AT. |
Keyboard interaction model
| Key | Behavior |
|---|---|
| Tab | Moves forward through Previous, each visible page link/button, and Next in document order. Disabled boundary controls are skipped. |
| Shift + Tab | Moves backward through the same controls. |
| Enter / Space | Activates the focused page link or Previous/Next control. |
Focus management rules
- Tab order follows visual/document order: Previous, then each visible page control, then Next.
- Disabled Previous/Next controls are removed from the tab order entirely, not just visually dimmed.
- The decorative ellipsis is never a Tab stop.
- For JS-driven (non-URL) pagination, move focus to or announce the newly loaded content region after each page change — don't leave focus stranded on a control that scrolled out of view.
WCAG 2.2 success criteria mapping
| SC | Name | Level | Why it applies |
|---|---|---|---|
| 2.1.1 | Keyboard | A | All functionality is operable through a keyboard interface with no specific timing. |
| 2.4.6 | Headings and Labels | AA | Headings and labels describe topic or purpose. |
| 4.1.2 | Name, Role, Value | A | For all UI components, name, role, and value are programmatically determinable; states and changes are announced. |