AccessScanRun a free scan

Guide

Accessible Pagination: Nav Landmarks, aria-current, and Keyboard Support

Pagination looks trivial, which is exactly why it's so often broken for assistive technology. A row of numbered links with "Previous" and "Next" is one of the most common patterns on the web, and one of the most commonly shipped without a landmark, without a current-page indicator that screen readers can announce, or without focus that survives a page change.

This guide shows how to build accessible pagination that works with keyboards and screen readers: a navigation landmark with an accessible name, aria-current="page" for the active page, link text that makes sense out of context, and predictable keyboard behavior. Every example is real markup you can paste and adapt, with the specific WCAG 2.2 success criteria each technique satisfies.

Wrap pagination in a labeled nav landmark

Pagination is navigation, so it belongs in a <nav> element. The native <nav> exposes an implicit role of navigation, which lets screen reader users jump straight to it via the landmarks rotor. But most pages have several navigation regions (primary menu, breadcrumbs, footer links), so an unnamed pagination nav announces as a generic "navigation" with no way to tell it apart.

Give it an accessible name with aria-label. Keep the label short and do not include the word "navigation" — screen readers already append the role, so aria-label="Pagination navigation" is announced as "Pagination navigation, navigation."

A semantic ordered list communicates that the pages have a sequence and lets screen readers announce position ("3 of 8"):

  • <nav aria-label="Pagination"> wraps the whole control
  • An <ol> (ordered list) holds the page items, since order is meaningful
  • Each page is an <li> containing an <a> for navigable pages
  • The current page uses aria-current="page" (covered below)

If you render two paginators on one page — say, above and below a long results table — give each a distinct label such as aria-label="Pagination, top" and aria-label="Pagination, bottom" so the landmark list isn't ambiguous.

Mark the current page with aria-current

A purely visual highlight — a filled background or bold weight on the active page number — tells sighted users where they are but is invisible to a screen reader. aria-current="page" is the correct, specific token for "this is the current page within a set of pagination links." Don't substitute aria-selected (that's for tabs, options, and grid cells) or aria-current="true" (the generic fallback, less precise here).

There's a real design decision about whether the current page should still be a link. The cleanest pattern is to render the current page as a non-link element so it isn't a redundant tab stop, while keeping it announced:

<li><span aria-current="page">3</span></li> for the active page, and <li><a href="?page=4">4</a></li> for the rest. A screen reader announces the active item as "current page, 3." Relying on aria-current rather than color alone also helps satisfy WCAG 1.4.1 Use of Color, since the state is no longer conveyed by appearance only.

If your framework re-renders the list on navigation, make sure exactly one element carries aria-current at any time. A stale or duplicated aria-current is a common bug in single-page apps where the old active item isn't cleared.

Write link text that works out of context

Screen reader users frequently pull all links on a page into a single list. In that list, a bare "2" or a chevron icon is meaningless, and ten links reading "1, 2, 3…" give no hint they're page numbers. WCAG 2.4.4 Link Purpose (In Context) and the stricter 2.4.9 Link Purpose (Link Only) both push toward self-describing links.

Add an accessible name that includes the page context without cluttering the visible UI. A common technique is a visually hidden suffix:

  • Visible "2" with aria-label="Go to page 2" so the link announces fully
  • "Previous" / "Next" as real words, not just ‹ › glyphs — if you use icons, give the link an aria-label like "Go to next page"
  • When Previous or Next is unavailable (you're on the first or last page), render it as a disabled <span> or a <button disabled>, not a dead <a> with no href

Avoid aria-disabled="true" on an anchor that still has an href — it announces as disabled but remains clickable and focusable, which is contradictory. Either remove the href (and the link role) or use a genuinely disabled button. Icon-only controls also need to meet WCAG 2.2's 2.5.8 Target Size (Minimum) of at least 24×24 CSS pixels, with adequate spacing so adjacent page numbers aren't hard to tap.

Keyboard support and focus management

Because we used native <a> and <button> elements, keyboard support is mostly free: links and buttons are focusable, Tab moves between them, and Enter (plus Space on buttons) activates them. This is the payoff for not reinventing controls with <div onclick> — those would require manual tabindex and key handlers and almost always ship with gaps. For the underlying principles, see keyboard accessibility.

The hard part is what happens after a click. There are two cases:

  • Full page navigation (each page is a real URL): the browser loads a new document and focus resets to the top — acceptable, and good for deep linking and SEO.
  • Client-side updates (SPA pagination that swaps content without a reload): focus is left on a button that may no longer exist, or the user is stranded with no announcement that results changed.

For client-side pagination, after the new results render, move focus to a sensible target — typically the results region heading or a container with tabindex="-1" — and announce the change. An ARIA live region (for example a polite status node saying "Showing results 21–40 of 160") covers WCAG 4.1.3 Status Messages so the update isn't silent. Do not steal focus mid-interaction or move it somewhere unexpected; move it once, deliberately, to the content the user asked for.

Finally, every focusable page link must have a clearly visible focus indicator (WCAG 2.4.7 Focus Visible), and in WCAG 2.2, 2.4.11 Focus Not Obscured (Minimum) means a sticky header or footer must not completely hide the focused pagination control. Never ship outline: none without a stronger replacement.

A complete, accessible example

Putting the pieces together, a robust paginator on page 3 of 8 looks like this in concept: a <nav aria-label="Pagination"> containing an <ol>; a "Previous" link to page 2; page-number links each labeled "Go to page N"; the current page as a <span aria-current="page">3</span>; and a "Next" link to page 4. On the last page, "Next" becomes a disabled button rather than an empty link.

This single pattern addresses landmark discoverability, current-state announcement, link clarity, color independence, target size, focus visibility, and (for SPAs) status messaging. None of it requires a library — just correct elements and a few ARIA attributes used precisely.

Pagination rarely gets tested because it "obviously works." It's worth a deliberate pass with a screen reader and keyboard, and an automated baseline to catch missing labels and color-only states. You can run a free first check with AccessScan, then verify the keyboard and screen reader behavior by hand — automated tools flag missing attributes, but only manual testing confirms the focus and announcement flow. For the legal backdrop driving this work across the EU, see the European Accessibility Act overview, and use the accessibility checklist to track pagination alongside the rest of your components.

Check your site against AccessScan

See your issues ranked by impact in seconds — free.

Run a free accessibility scan

FAQ

Should the current page still be a clickable link?

It's cleaner to render the current page as a non-link element (such as a span) with aria-current="page". This avoids a redundant tab stop and a link that navigates to the page you're already on, while still being announced as "current page." If you keep it as a link for styling reasons, it must still carry aria-current="page".

What's the difference between aria-current="page" and aria-current="true"?

aria-current="page" is the specific token for the current item within a set of pagination or navigation links, and screen readers announce it as "current page." aria-current="true" is the generic fallback announced as "current." For pagination, the more precise "page" value is the correct choice.

Do I need a nav landmark if there's only one paginator on the page?

Yes. A <nav> with an aria-label like "Pagination" lets screen reader users find and identify the control via the landmarks list, and distinguishes it from other navigation regions. The label costs nothing and removes ambiguity if a second paginator is ever added.

How should disabled Previous/Next controls be handled?

Use a disabled <button> or a non-interactive <span>, not an <a> without an href. Avoid aria-disabled="true" on a link that still has an href, since it announces as disabled but remains clickable. A genuinely disabled button is removed from the tab order and announced correctly.

What needs to happen to focus in single-page-app pagination?

After the new results render, move focus once to a meaningful target such as the results heading or a container with tabindex="-1", and announce the change with an ARIA live region (WCAG 4.1.3). On full page reloads the browser resets focus for you, so manual management is only needed for client-side updates.

More guides