# Pagination

> Page links from a nav with an ordered list, with a current page, disabled edges and no script.

Page: https://pico.rusticstack.com/docs/extras/pagination

Pagination is a `nav` with an `ol` of links. The current page is `aria-current="page"`, and a disabled edge is a `span` with `role="link"` and `aria-disabled="true"`. No class and no JavaScript are needed, because the server normally knows the current page.

Load `css/extras/pagination.min.css`, or the whole set from `css/pico.extras.min.css`. See the [overview](/docs/extras/overview).

## Basic pagination

The hook is `aria-label="Pagination"` on the `nav` (the match ignores case). Page numbers and ellipses, which are the first, last, current and neighboring pages, come from your app logic. An ellipsis is an `li` with `aria-hidden="true"`.

**Example: Pagination**

```html
<nav aria-label="Pagination">
  <ol>
    <li><a href="?page=1" rel="prev">Previous</a></li>
    <li><a href="?page=1" aria-label="Page 1">1</a></li>
    <li><a href="?page=2" aria-label="Page 2" aria-current="page">2</a></li>
    <li><a href="?page=3" aria-label="Page 3">3</a></li>
    <li aria-hidden="true">&hellip;</li>
    <li><a href="?page=10" aria-label="Page 10">10</a></li>
    <li><a href="?page=3" rel="next">Next</a></li>
  </ol>
</nav>
```

## With a results summary

Put the summary and the `nav` in one flex row, summary at the start and pages at the end. The row wraps on a narrow screen.

**Example: Pagination with a summary**

```html
<div style="display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--pico-spacing)">
  <p style="margin: 0; color: var(--pico-muted-color)">Showing 11 to 20 of 96 invoices</p>
  <nav aria-label="Pagination" style="margin: 0">
    <ol>
      <li><a href="?page=1" rel="prev">Previous</a></li>
      <li><a href="?page=1" aria-label="Page 1">1</a></li>
      <li><a href="?page=2" aria-label="Page 2" aria-current="page">2</a></li>
      <li><a href="?page=3" aria-label="Page 3">3</a></li>
      <li><a href="?page=3" rel="next">Next</a></li>
    </ol>
  </nav>
</div>
```

## Disabled edges

On the first page, the previous item is not a link, because there is nowhere to go. Use a `span` with `role="link"` and `aria-disabled="true"`. It stays in the reading order, dimmed and unclickable.

**Example: First page**

```html
<nav aria-label="Team members pages" data-pagination>
  <ol>
    <li><span role="link" aria-disabled="true">Previous</span></li>
    <li><a href="?page=1" aria-label="Page 1" aria-current="page">1</a></li>
    <li><a href="?page=2" aria-label="Page 2">2</a></li>
    <li><a href="?page=3" aria-label="Page 3">3</a></li>
    <li aria-hidden="true">&hellip;</li>
    <li><a href="?page=10" aria-label="Page 10">10</a></li>
    <li><a href="?page=2" rel="next">Next</a></li>
  </ol>
</nav>
```

## Another label

The default hook is `aria-label="Pagination"`. When the label says something else, so that a page with several lists can tell them apart, add `data-pagination` to the `nav`. The first example above uses the label, this one uses the attribute.

## Icon-only previous and next

Hide the arrow with `aria-hidden="true"` and name the link with `aria-label`. Arrows on a link with `rel` flip in right to left pages.

**Example: Icon pagination**

```html
<nav aria-label="Projects pages" data-pagination>
  <ol>
    <li>
      <a href="?page=1" rel="prev" aria-label="Previous page"><span aria-hidden="true">&larr;</span></a>
    </li>
    <li><a href="?page=1" aria-label="Page 1">1</a></li>
    <li><a href="?page=2" aria-label="Page 2" aria-current="page">2</a></li>
    <li><a href="?page=3" aria-label="Page 3">3</a></li>
    <li>
      <a href="?page=3" rel="next" aria-label="Next page"><span aria-hidden="true">&rarr;</span></a>
    </li>
  </ol>
</nav>
```

## Size

Set `--pico-pagination-item-size` on the `nav` for a compact or large list.

**Example: Compact pagination**

```html
<nav aria-label="Compact pages" data-pagination style="--pico-pagination-item-size: 1.75rem">
  <ol>
    <li>
      <a href="?page=1" rel="prev" aria-label="Previous page"><span aria-hidden="true">&larr;</span></a>
    </li>
    <li><a href="?page=1" aria-label="Page 1">1</a></li>
    <li><a href="?page=2" aria-label="Page 2" aria-current="page">2</a></li>
    <li><a href="?page=3" aria-label="Page 3">3</a></li>
    <li>
      <a href="?page=3" rel="next" aria-label="Next page"><span aria-hidden="true">&rarr;</span></a>
    </li>
  </ol>
</nav>
```

## Updating without navigation

If the list changes without a page load, move `aria-current` from script. The page numbers themselves still come from your app.

```js
const nav = document.querySelector('nav[aria-label="Pagination"]');
function setCurrent(page) {
  nav.querySelectorAll('a[aria-current="page"]').forEach((a) => a.removeAttribute("aria-current"));
  const next = nav.querySelector('a[aria-label="Page ' + page + '"]');
  if (next) next.setAttribute("aria-current", "page");
}
```

## Accessibility

- The `nav` needs a label, and each list on a page needs its own.
- Name number links with `aria-label="Page 2"`, and mark the current one with `aria-current="page"`.
- Use `rel="prev"` and `rel="next"` on the edge links.
- The current page keeps a border, so it still shows under forced colors.
- Hide ellipses from assistive technology with `aria-hidden="true"`.

## Customization

| Variable | Default | Description |
| --- | --- | --- |
| `--pico-pagination-item-size` | `2.25rem` | Minimum width and height of each item. |
| `--pico-pagination-gap` | `0.25rem` | Space between items. |

