Pagination

Splits a long list of data into pages, with page number buttons and previous/next triggers.
Installation
Expand

npx @left4code/cosmic-ui-cli@latest add pagination
 
Expand

This writes the component and everything it imports, then installs the packages it needs. Run npx @left4code/cosmic-ui-cli@latest initfirst if you haven't already.

Install the following dependencies:

pnpm
npm
yarn
bun
yarn add @zag-js/pagination @zag-js/react

Built directly on the Zag.js state machine. This is a single self-contained component rather than a Root + parts split - the page buttons come from api.pages, an array the machine computes from count / pageSize / siblingCount, so there's nothing left for a consumer to compose by hand. The plain border shown here is a placeholder; swap it for the Frame primitive later. Copy and paste the following code into your project.

components/ui/pagination.tsx
Expand

import { useId } from "react";
import { twMerge } from "tailwind-merge";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { useMachine, normalizeProps } from "@zag-js/react";
import * as paginationMachine from "@zag-js/pagination";

function Pagination({
  className,
  ...rest
}: Partial<paginationMachine.Props> & { className?: string }) {
  const service = useMachine(paginationMachine.machine, { id: useId(), ...rest });
  const api = paginationMachine.connect(service, normalizeProps);

  return (
    <div {...api.getRootProps()} className={twMerge(["flex items-center gap-1.5", className])}>
      <button
        {...api.getPrevTriggerProps()}
        className="size-8 flex items-center justify-center border border-primary/30 hover:bg-primary/10 disabled:opacity-30 disabled:pointer-events-none"
      >
        <ChevronLeft className="size-4" />
      </button>
      {api.pages.map((page, i) =>
        page.type === "page" ? (
          <button
            key={i}
            {...api.getItemProps(page)}
            className="size-8 border border-primary/30 hover:bg-primary/10 data-[selected]:bg-primary/20 data-[selected]:border-primary"
          >
            {page.value}
          </button>
        ) : (
          <span
            key={i}
            {...api.getEllipsisProps({ index: i })}
            className="size-8 flex items-center justify-center"
          >
            &#8230;
          </span>
        )
      )}
      <button
        {...api.getNextTriggerProps()}
        className="size-8 flex items-center justify-center border border-primary/30 hover:bg-primary/10 disabled:opacity-30 disabled:pointer-events-none"
      >
        <ChevronRight className="size-4" />
      </button>
    </div>
  );
}

export { Pagination };
 
Expand

Update the import paths to match your project setup.

Usage
Expand

import { Pagination } from "@/components/ui/pagination";
 
Expand
Expand

<Pagination count={100} defaultPageSize={10} siblingCount={1} />
 
Expand
Powered by synthetic caffeine · Deployed by Left4code · Signal traceable on GitHub.