# Numbered pagination (themes.dev UI block)

Previous and next with icons, page numbers with a gap, the current page marked and the ends disabled, with a line about what is shown. Black and white, dark mode, shadcn-compatible; React, Astro and plain HTML.

38% of the 2,073 sites in The UI Index have a navigation (790 sites).

## Install

- React: `npx shadcn@latest add https://www.themes.dev/ui/r/numbered-pagination.json`
- Astro: `npx shadcn@latest add https://www.themes.dev/ui/r/astro/numbered-pagination.json`
- HTML: https://www.themes.dev/ui/r/html/numbered-pagination.html (with https://www.themes.dev/ui/r/html/caseroom.css)

Page: https://www.themes.dev/ui/blocks/numbered-pagination/ · Rules for agents: https://www.themes.dev/ui/AGENT.md

## Code

### numbered-pagination.astro

```astro
---
import Pagination from '@/components/ui/pagination.astro';
import { cn } from '@/lib/utils';
import { numberedPagination as c } from './numbered-pagination.classes';

interface Props {
  current: number;
  total: number;
  /** The address of a page, e.g. (p) => `/blog/page/${p}/`. */
  href: (page: number) => string;
  /** The accessible name of the nav; give each pager on a page its own. */
  label?: string;
  /** A line before the pages, e.g. "Showing 41 to 50 of 118 posts". */
  summary?: string;
  class?: string;
}
const { current, total, href, label = 'Pages', summary, class: className } = Astro.props;
---

<div class={cn(c.root, className)}>
  {summary && <p class={c.summary}>{summary}</p>}
  <Pagination current={current} total={total} href={href} label={label} class={c.nav} />
</div>
```

### numbered-pagination.classes.ts

```tsx
/** Numbered pagination as a section: previous and next with icons, the page numbers with a gap (…) where pages are
 *  skipped, the current page marked (aria-current) and the ends disabled. The links are the pagination item; this
 *  adds the row with a short line about what is on the page. Narrow rows swap the numbers for 'Page 5 of 12'. */
export const numberedPagination = {
  root: 'mx-auto flex w-full max-w-6xl flex-col gap-3 px-6 py-6 @container',
  summary: 'm-0 text-sm text-muted-foreground tabular-nums',
  nav: 'w-full',
};
```

### numbered-pagination.tsx

```tsx
import { Pagination } from '@/components/ui/pagination';
import { cn } from '@/lib/utils';
import { numberedPagination as c } from './numbered-pagination.classes';

export interface NumberedPaginationProps {
  current: number;
  total: number;
  /** The address of a page, e.g. (p) => `/blog/page/${p}/`. */
  href: (page: number) => string;
  /** The accessible name of the nav; give each pager on a page its own. */
  label?: string;
  /** A line before the pages, e.g. "Showing 41 to 50 of 118 posts". */
  summary?: string;
  className?: string;
}

export function NumberedPagination({ current, total, href, label = 'Pages', summary, className }: NumberedPaginationProps) {
  return (
    <div className={cn(c.root, className)}>
      {summary && <p className={c.summary}>{summary}</p>}
      <Pagination current={current} total={total} href={href} label={label} className={c.nav} />
    </div>
  );
}
```
