# V pagination 18

> Loading/disabled state

A shadcn/ui block from the [@cnippet](https://ui.cnippet.dev/) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/cnippet/v-pagination-18).

- Page, with a live preview: https://shadcn.exchange/r/cnippet/v-pagination-18
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/cnippet/v-pagination-18-light-0091086c4b6a13cb.png
- Registry item: https://ui.cnippet.dev/r/v-pagination-18.json
- Categories: pagination
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://ui.cnippet.dev/r/v-pagination-18.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `@cnippet/pagination`

## Files

### `registry/default/variants/v-pagination-18.tsx`

```tsx
"use client";

import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
import type React from "react";
import { useState } from "react";
import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
} from "@/registry/default/ui/pagination";

const TOTAL_PAGES = 20;

function getVisiblePages(current: number, total: number): (number | "...")[] {
  if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);
  if (current <= 4) return [1, 2, 3, 4, 5, "...", total];
  if (current >= total - 3)
    return [1, "...", total - 4, total - 3, total - 2, total - 1, total];
  return [1, "...", current - 1, current, current + 1, "...", total];
}

export function Pattern() {
  const [page, setPage] = useState(5);
  const pages = getVisiblePages(page, TOTAL_PAGES);

  function go(e: React.MouseEvent, p: number) {
    e.preventDefault();
    setPage(p);
  }

  return (
    <Pagination>
      <PaginationContent>
        <PaginationItem>
          <PaginationLink
            aria-disabled={page === 1}
            aria-label="Go to previous page"
            className={page === 1 ? "pointer-events-none opacity-50" : ""}
            href="#"
            onClick={(e) => go(e, page - 1)}
            size="icon"
          >
            <ChevronLeftIcon className="size-4" />
          </PaginationLink>
        </PaginationItem>
        {pages.map((p, idx) =>
          p === "..." ? (
            <PaginationItem key={`ellipsis-${idx}`}>
              <PaginationEllipsis />
            </PaginationItem>
          ) : (
            <PaginationItem key={p}>
              <PaginationLink
                href="#"
                isActive={p === page}
                onClick={(e) => go(e, p as number)}
              >
                {p}
              </PaginationLink>
            </PaginationItem>
          ),
        )}
        <PaginationItem>
          <PaginationLink
            aria-disabled={page === TOTAL_PAGES}
            aria-label="Go to next page"
            className={
              page === TOTAL_PAGES ? "pointer-events-none opacity-50" : ""
            }
            href="#"
            onClick={(e) => go(e, page + 1)}
            size="icon"
          >
            <ChevronRightIcon className="size-4" />
          </PaginationLink>
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  );
}
```
