Skip to content

Pagination Strategies

Cursor-Based Pagination

Bagisto GraphQL uses cursor-based pagination for efficient data fetching.

How It Works

Advantages:

  • ✅ Consistent results even when data changes
  • ✅ No duplicate items across pages
  • ✅ Efficient for large datasets
  • ✅ Supports both forward and backward navigation

Pagination Component

ts

"use client";

import { usePathname, useSearchParams, useRouter } from "next/navigation";

export default function Pagination({
  itemsPerPage,
  itemsTotal,
  currentPage,
  nextCursor,
}) {
  const router = useRouter();
  const pathname = usePathname();
  const currentParams = useSearchParams();

  const pageCount = Math.ceil(itemsTotal / itemsPerPage);

  const handlePageClick = (page: number) => {
    if (page < 0 || page >= pageCount) return;

    const params = new URLSearchParams(currentParams.toString());
    params.set("page", String(page + 1));

    // Store cursor for next page
    if (page === currentPage + 1 && nextCursor) {
      params.set("cursor", nextCursor);
    } else {
      params.delete("cursor");
    }

    const newUrl = createUrl(pathname, params);
    router.replace(newUrl);
  };

  return (
    <ul className="flex gap-2">
      {/* Previous Button */}
      <li>
        <button
          onClick={() => handlePageClick(currentPage - 1)}
          disabled={currentPage <= 0}
        >
          Previous
        </button>
      </li>

      {/* Page Numbers */}
      {renderPageButtons()}

      {/* Next Button */}
      <li>
        <button
          onClick={() => handlePageClick(currentPage + 1)}
          disabled={currentPage >= pageCount - 1}
        >
          Next
        </button>
      </li>
    </ul>
  );
}

Implementing Pagination in Search Page

ts
export default async function SearchPage({ searchParams }) {
  const params = await searchParams;
  const { page, cursor } = params;
  const itemsPerPage = 12;
  const currentPage = page ? parseInt(page) - 1 : 0;

  let currentAfterCursor = cursor;

  // If navigating to a page without a cursor, calculate it
  if (currentPage > 0 && !cursor) {
    const cursorData = await graphqlRequest(GET_PRODUCTS_PAGINATION, {
      query: searchValue,
      first: currentPage * itemsPerPage,
      sortKey: "CREATED_AT",
      reverse: true,
    });
    currentAfterCursor = cursorData?.products?.pageInfo?.endCursor;
  }

  // Fetch products for current page
  const data = await graphqlRequest(GET_PRODUCTS, {
    query: searchValue,
    first: itemsPerPage,
    sortKey: "CREATED_AT",
    reverse: true,
    ...(currentAfterCursor && { after: currentAfterCursor }),
  });

  const products = data?.products?.edges?.map((e) => e.node) || [];
  const pageInfo = data?.products?.pageInfo;
  const totalCount = data?.products?.totalCount;

  return (
    <div>
      <ProductGrid products={products} />
      
      {totalCount > itemsPerPage && (
        <Pagination
          itemsPerPage={itemsPerPage}
          itemsTotal={totalCount}
          currentPage={currentPage}
          nextCursor={pageInfo?.endCursor}
        />
      )}
    </div>
  );
}

Released under the MIT License.