zfb

Type to search...

to open search from anywhere

paginate

CreatedJun 1, 2026Takeshi Takatsudo

Expand a list of items into paginated route instances.

Signature

paginate<T, K extends string = string>(
  items: readonly T[],
  opts: PaginateOptions<K>,
): PaginateRoute<T, K>[]

Use paginate inside a paths() export to fan a list of items out across a paginated route. The function chunks items by pageSize and produces one PaginateRoute per chunk, each carrying the URL params and page props the component receives.

paginate always emits at least one route — an empty input list yields a single empty page so the index route still renders rather than 404ing.

PaginateOptions

type PaginateOptions<K extends string = string> = {
  pageSize: number;
  param: K;
};
  • pageSize — items per page. Must be a positive integer.
  • param — name of the dynamic URL segment to fill (e.g. "page" for a route file named [page].tsx).

PaginateRoute shape

type PaginateRoute<T, K extends string = string> = {
  params: Record<K, string>;
  props: { page: PaginatedPage<T> };
};

Each route carries:

  • params — URL segment values. For a param: "page" call, this is { page: "1" }, { page: "2" }, etc.
  • props.page — a PaginatedPage<T> record with:
    • data: T[] — items belonging to this page.
    • page: number — 1-based page number.
    • lastPage: number — total number of pages.
    • pageSize: number — items per page (echoed for convenience).
    • total: number — total item count across all pages.

Example

Paginate a blog index 10 posts at a time under /blog/page/[page].tsx.

import { paginate } from "zfb/paginate";
import { getCollection } from "zfb/content";

export function paths() {
  const posts = getCollection("blog");
  return paginate(posts, {
    pageSize: 10,
    param: "page",
  });
}

export default function BlogPage({ page }) {
  const { data: items, page: currentPage, lastPage } = page;
  return (
    <section>
      <h2>Blog — page {currentPage} of {lastPage}</h2>
      <ul>
        {items.map((post) => (
          <li key={post.slug}>
            <a href={`/blog/${post.slug}`}>{post.data.title}</a>
          </li>
        ))}
      </ul>
    </section>
  );
}

The param value ("page") must match the dynamic segment name in your route filename ([page].tsx). Page numbers start at 1 and are passed as strings in params.

The page component receives the PaginatedPage record as props.page. Destructure it to access data, page, lastPage, pageSize, and total.

Revision History