labsUI

Pagination

Server-safe page links from page and total: previous/next, ellipsis gaps and a sliding active pill, with numbers that swipe the way you moved. Links by default; buttons with onPageChange. The row keeps a fixed width so it never jumps.

npx @kznlabs/labsui add pagination

Page 6 of 12

import { Pagination } from "@/components/ui/pagination";// Server page: plain links, so paging works without JavaScript.export function PostsPager({ page, total }: { page: number; total: number }) {  return (    <Pagination page={page} total={total} href={(p) => `/blog?page=${p}`} />  );}

<Pagination>

Page links (or buttons) with previous and next. The active pill slides between pages and the numbers swipe the way you moved. The row keeps its width: gaps become an ellipsis.

  • page*
    number

    Current page, from 1.

  • total*
    number

    Number of pages.

  • hrefdefault (page) => `?page=${page}`
    (page: number) => string

    URL of a page, for links that work without JS. Default ?page=N. Unused with onPageChange.

  • labels
    { previous?: ReactNode; next?: ReactNode; }

    Text of the previous and next controls; phones show the arrows only.

  • onPageChange
    (page: number) => void

    Called with the picked page; the controls become buttons. From client parents only.

  • siblingsdefault 1
    number

    Pages kept on each side of the current one.

PropTypeDefault
page*

Current page, from 1.

number—
total*

Number of pages.

number—
href

URL of a page, for links that work without JS. Default ?page=N. Unused with onPageChange.

(page: number) => string(page) => `?page=${page}`
labels

Text of the previous and next controls; phones show the arrows only.

{ previous?: ReactNode; next?: ReactNode; }—
onPageChange

Called with the picked page; the controls become buttons. From client parents only.

(page: number) => void—
siblings

Pages kept on each side of the current one.

number1

Also accepts every <nav> prop.

pageRange()

Page numbers to render, with "…" for gaps. Keeps first, last and siblings pages on each side of page, and always returns the same length so the bar never changes width. Key items by index so each slot stays mounted and its number swipes instead of remounting. E.g. pageRange(6, 12) returns [1, "…", 5, 6, 7, "…", 12].

pageRange(page: number, total: number, siblings?: number) => (number | "…")[]

* required. Every component also takes className.