Skip to content

Pagination

Move through pages of results and show the current page.

import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use pagination when position matters: tables, logs and search results that people come back to, share by URL, or count through. Don't use it for a feed people skim, where infinite scroll or a "Load more" button reads better, or for a list short enough to show whole.

import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
  paginationRange,
} from '@youj/react'

export function Pager({ page, pageCount }: { page: number; pageCount: number }) {
  return (
    <Pagination>
      <PaginationContent>
        <PaginationItem>
          <PaginationPrevious href={`?page=${page - 1}`} disabled={page === 1} />
        </PaginationItem>
        {paginationRange(page, pageCount).map((item) =>
          typeof item === 'number' ? (
            <PaginationItem key={item}>
              <PaginationLink href={`?page=${item}`} isActive={item === page} aria-label={`Page ${item}`}>
                {item}
              </PaginationLink>
            </PaginationItem>
          ) : (
            <PaginationItem key={item}>
              <PaginationEllipsis />
            </PaginationItem>
          ),
        )}
        <PaginationItem>
          <PaginationNext href={`?page=${page + 1}`} disabled={page === pageCount} />
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  )
}

Every page is a real link, so pages can be bookmarked, shared and opened in a new tab. To page without a navigation, handle onClick, call event.preventDefault() and update your state, as the Demo story does. The raised key under the current page is one indicator for the whole control, and it glides to whichever link has isActive.

Reference

PropsisActive, size, disabled… (7)

Props

PaginationLink props
PropType
isActiveboolean–
disabledboolean

The edge of the range: aria-disabled, no href and inert to clicks, but still focusable, so paging onto the last page with Enter keeps focus on the control instead of dropping it to the body.

–
size'default' | 'icon'

default 'icon'

'icon'

PaginationPrevious and PaginationNext

They take every PaginationLink prop, and text for the label beside the chevron.

PaginationNext props
PropType
isActiveboolean–
disabledboolean

The edge of the range: aria-disabled, no href and inert to clicks, but still focusable, so paging onto the last page with Enter keeps focus on the control instead of dropping it to the body.

–
size'default' | 'icon'–
textstring–

Other parts

Pagination is a nav named "Pagination", PaginationContent a ul, PaginationItem an li and PaginationEllipsis a span. Each takes the props of its element.

MotionCurrent page, previous and Next, hover… · reduced motion

Motion

Motion tokens
MomentTokens
Current pagethe raised key glides to the new page, transform only
spring.smooth

0.36s, bounce 0

0.36s, bounce 0
Previous and Nextthe chevron nudges 2px toward where it goes, on hover
duration.fastease.standard

140ms · cubic-bezier(0.2, 0, 0, 1)

140mscubic-bezier(0.2, 0, 0, 1)
Hovercolor and background
duration.fastease.standard

140ms · cubic-bezier(0.2, 0, 0, 1)

140mscubic-bezier(0.2, 0, 0, 1)
Pressscales to 0.97 while held
duration.instantease.standard

80ms · cubic-bezier(0.2, 0, 0, 1)

80mscubic-bezier(0.2, 0, 0, 1)

Reduced motion

The key jumps to the new page, the chevrons shift without easing, and links don't scale on press.

In right-to-left layouts the chevrons point, and nudge, the other way.

Reduced motion

Paging works the same. The raised key jumps to the new page instead of gliding, the chevrons shift without easing, and links don't scale on press.

AccessibilityKeyboard, ARIA, focus

Accessibility

  • Pagination is a nav landmark. Its default name is "Pagination"; give it a more specific aria-label ("Invoice pages") when a page has more than one.
  • The current page's link carries aria-current="page".
  • Give number links an aria-label such as "Page 3", so they're announced as pages, not bare numbers. Previous and Next are named "Go to previous page" and "Go to next page".
  • Ellipses and the raised key are hidden from assistive technology. The numbers either side of a gap already say pages were skipped.
  • Tab moves through the links, and Enter follows one. A disabled Previous or Next is still a Tab stop, announced as unavailable, so focus never falls back to the page.