Skip to content
Ndop UI

Pagination

Navigates between pages of results.

  • v1.0.0
  • SSR
  • Zoneless
  • 1.4 kB gzip
import { Component } from '@angular/core';
import { NdIcon } from '@ndop/core';
import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@/ui/pagination/pagination';

/** Links to pages, with the current one marked. */
@Component({
  selector: 'example-pagination-demo',
  imports: [
    NdIcon,
    Pagination,
    PaginationContent,
    PaginationEllipsis,
    PaginationItem,
    PaginationLink,
    PaginationNext,
    PaginationPrevious,
  ],
  template: `
    <nav ndPagination>
      <ul ndPaginationContent>
        <li ndPaginationItem>
          <a ndPaginationPrevious href="#page-1">
            <svg ndIcon="chevron-left"></svg>
            <span class="hidden sm:block">Previous</span>
          </a>
        </li>
        <li ndPaginationItem><a ndPaginationLink href="#page-1">1</a></li>
        <li ndPaginationItem>
          <a ndPaginationLink href="#page-2" active>2</a>
        </li>
        <li ndPaginationItem><a ndPaginationLink href="#page-3">3</a></li>
        <li ndPaginationItem>
          <span ndPaginationEllipsis><svg ndIcon="ellipsis"></svg></span>
        </li>
        <li ndPaginationItem>
          <a ndPaginationNext href="#page-3">
            <span class="hidden sm:block">Next</span>
            <svg ndIcon="chevron-right"></svg>
          </a>
        </li>
      </ul>
    </nav>
  `,
})
export class PaginationDemoExample {}

Pagination moves between pages of results. It's a nav landmark named "Pagination" with a list of links, styled as buttons: ghost for other pages, outlined for the current one, which is marked aria-current="page". The previous and next links are named for screen readers, and their chevrons turn around in right-to-left layouts.

Installation

npx @ndop/cli add pagination

Usage

import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@/ui/pagination/pagination';
<nav ndPagination>
  <ul ndPaginationContent>
    <li ndPaginationItem>
      <a ndPaginationPrevious href="#page-1">
        <svg ndIcon="chevron-left"></svg>
        <span class="hidden sm:block">Previous</span>
      </a>
    </li>
    <li ndPaginationItem><a ndPaginationLink href="#page-1">1</a></li>
    <li ndPaginationItem>
      <a ndPaginationLink href="#page-2" active>2</a>
    </li>
    <li ndPaginationItem><a ndPaginationLink href="#page-3">3</a></li>
    <li ndPaginationItem>
      <span ndPaginationEllipsis><svg ndIcon="ellipsis"></svg></span>
    </li>
    <li ndPaginationItem>
      <a ndPaginationNext href="#page-3">
        <span class="hidden sm:block">Next</span>
        <svg ndIcon="chevron-right"></svg>
      </a>
    </li>
  </ul>
</nav>

Examples

Pagination state

Pages kept in component state, with buttons.

Showing results 1 to 10 of 50

import { Component, computed, signal } from '@angular/core';
import { NdIcon } from '@ndop/core';
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@/ui/pagination/pagination';

/**
 * Pages kept in component state: buttons instead of links, with the
 * current page marked and the arrows disabled at either end.
 */
@Component({
  selector: 'example-pagination-state',
  imports: [
    NdIcon,
    Pagination,
    PaginationContent,
    PaginationItem,
    PaginationLink,
    PaginationNext,
    PaginationPrevious,
  ],
  template: `
    <div class="grid gap-4">
      <p class="text-center text-sm text-muted-foreground" aria-live="polite">
        Showing results {{ (page() - 1) * 10 + 1 }} to {{ page() * 10 }} of 50
      </p>
      <nav ndPagination label="Results pages">
        <ul ndPaginationContent>
          <li ndPaginationItem>
            <button
              ndPaginationPrevious
              [disabled]="page() === 1"
              (click)="page.set(page() - 1)"
            >
              <svg ndIcon="chevron-left"></svg>
              <span class="hidden sm:block">Previous</span>
            </button>
          </li>
          @for (number of pages(); track number) {
            <li ndPaginationItem>
              <button
                ndPaginationLink
                [active]="number === page()"
                (click)="page.set(number)"
              >
                {{ number }}
              </button>
            </li>
          }
          <li ndPaginationItem>
            <button
              ndPaginationNext
              [disabled]="page() === count"
              (click)="page.set(page() + 1)"
            >
              <span class="hidden sm:block">Next</span>
              <svg ndIcon="chevron-right"></svg>
            </button>
          </li>
        </ul>
      </nav>
    </div>
  `,
})
export class PaginationStateExample {
  protected readonly count = 5;
  protected readonly page = signal(1);
  protected readonly pages = computed(() =>
    Array.from({ length: this.count }, (_, index) => index + 1),
  );
}

Accessibility

Pagination has no keyboard interaction or focus handling of its own; it keeps the semantics of the element it's on.

API reference

Pagination

nav[ndPagination]

Page navigation for a list of results, as a nav landmark.

Pagination binding Type Default
[label]
Name of the landmark.
string'Pagination'
[class]
Extra classes, merged last so they win.
string''
Data attribute Values
data-slotpagination

PaginationContent

ul[ndPaginationContent]

The list of a pagination's links.

PaginationContent binding Type Default
[class]
Extra classes, merged last so they win.
string''
Data attribute Values
data-slotpagination-content

PaginationItem

li[ndPaginationItem]

One entry of a pagination: a link, or an ellipsis.

Data attribute Values
data-slotpagination-item
a[ndPaginationLink], button[ndPaginationLink]

A link to one page, styled as a ghost button, or an outline one for the current page, which it marks aria-current="page". On a button, it calls your handler instead of navigating.

PaginationLink binding Type Default
[active]
Whether it's the current page.
booleanfalse
[size]
Button size.
'default' | 'xs' | 'sm' | 'lg' | 'icon' | 'icon-xs' | 'icon-sm' | 'icon-lg''icon'
[class]
Extra classes, merged last so they win.
string''
Data attribute Values
data-slotpagination-link
data-active —

PaginationPrevious

a[ndPaginationPrevious], button[ndPaginationPrevious]

The link to the previous page. Put a chevron-left icon and the word "Previous" in it; the icon turns around in right-to-left layouts.

PaginationPrevious binding Type Default
[class]
Extra classes, merged last so they win.
string''
Data attribute Values
data-slotpagination-link

PaginationNext

a[ndPaginationNext], button[ndPaginationNext]

The link to the next page. Put the word "Next" and a chevron-right icon in it; the icon turns around in right-to-left layouts.

PaginationNext binding Type Default
[class]
Extra classes, merged last so they win.
string''
Data attribute Values
data-slotpagination-link

PaginationEllipsis

[ndPaginationEllipsis]

Stands for pages left out of a long range; hidden from assistive technology. Put an ellipsis icon in it.

PaginationEllipsis binding Type Default
[class]
Extra classes, merged last so they win.
string''
Data attribute Values
data-slotpagination-ellipsis

Agent notes

Do

  • Use links with an href (or routerLink) when each page has a URL, so pages can be bookmarked and opened in a new tab.
  • Use button[ndPaginationLink] when the page lives only in component state, and keep active in sync.
  • Show the first, last and nearby pages, with ndPaginationEllipsis for the gaps.

Don't

  • Don't paginate a short list that fits on one page.
  • Don't leave out the previous and next links on long ranges.