Pagination

There is a small amount of plumbing that you will need to do in the shop in order to get pagination working. The most complex parts have been abstracted away in to a Core component called PaginationProvider, which in most cases you will not need to use directly. This component handles getting and setting the correct URL parameters for pagination (?page=5 etc).

Setting up pagination

The example below is for a category page, but can be adapted for any page that uses pagination.

You will need to use the PaginationContext from Core, and the PaginationBehaviour from UI.

Copy
Copied
import PaginationContext from "@jetshop/core/components/Pagination/PaginationContext";
import { PaginationBehaviour } from "@jetshop/ui/Pagination";

PaginationContext provides some render prop arguments that PaginationBehaviour needs in order to set up the UI elements for controlling pagination. You'll need to use the Consumer from PaginationContext to pass through the required values:

Copy
Copied
<PaginationContext.Consumer>
  {({ currentPage, goToPage, perPage, setProductsPerPage }) => (
    <PaginationBehaviour
      currentPage={currentPage}
      goToPage={goToPage}
      perPage={perPage}
      total={category.products.totalResults}
    >
      {(paginationProps) => <Pagination {...paginationProps} />}
    </PaginationBehaviour>
  )}
</PaginationContext.Consumer>

As you can see, we take the currentPage, goToPage fn, and perPage, and pass them straight in to the PaginationBehaviour component, along with the category's totalResults field (from the category route query). The full interface of PaginationContext is as follows:

Copy
Copied
interface PaginationState {
  /** The current page number */
  currentPage: number;
  /**
   * Current offset (e.g. if we are on the second page and there are 10 items
   * per page, this will be 10)
   */
  offset: number;
  /** Number of items displayed per page */
  perPage: number;
  /** Navigate to given page number */
  goToPage: (page: number) => void;
  /** Set amount of products per page */
  setProductsPerPage: (perPage: number) => void;
}

Note that you don't have to directly wrap PaginationBehaviour like we have above. If you like, you could wrap your entire Category page with PaginationContext.Consumer, and then use the currentPage somewhere for display purposes.

PaginationBehaviour

Copy
Copied
import { PaginationBehaviour } from "@jetshop/ui/Pagination";

This component renders its children with some arguments that help to construct the actual pagination UI. The full interface is as follows:

Copy
Copied
interface IPaginationBehaviourRenderProps {
  /** The total number of items */
  totalItems: number;
  /** The total number of pages */
  totalPages: number;
  /** The current page number */
  currentPage: number;
  /** Navigate to the next page */
  nextPage: () => void;
  /** Navigate to the previous page */
  prevPage: () => void;
  /** Navigate to given page number */
  goToPage: (page: number) => void;
  // Update number of products displayed per page
  setProductsPerPage: (perPage: number) => void;
  /** Whether the current page is the first */
  isFirstPage: boolean;
  /** Whether the current page is the last */
  isLastPage: boolean;
  /** Whether there are pages to render */
  hasPages: boolean;
}

Pagination (UI)

In the example at the top of this document, we've passed all of these args directly in to the Pagination component. This is a basic pagination implementation in UI:

Copy
Copied
import { Pagination } from "@jetshop/ui/Pagination";

This will render a Previous button, a Next button, and a Page {currentPage} of {totalPages} readout. Of course you are free to use your own component for pagination by piecing together the arguments provided by PaginationBehaviour. The basic implementation in UI is shown below:

Copy
Copied
return hasPages ? (
  <>
    <button onClick={prevPage} disabled={isFirstPage}>
      {formatMessage("Prev")}
    </button>
    <div>
      {formatMessage(`Page {currentPage} of {totalPages}`, {
        currentPage,
        totalPages,
      })}
    </div>
    <button onClick={nextPage} disabled={isLastPage}>
      {formatMessage("Next")}
    </button>
  </>
) : null;

Setting products per page

By default the pagination is set to display 16 products per page. In order to change the default value, supply the defaultProductsPerPage prop to the PaginationProvider in your Shop.js file. If you don't have a PaginationProvider in there, you're based on an older version of the framework and you can just add it.

Copy
Copied
import PaginationProvider from "@jetshop/core/components/Pagination/PaginationProvider";

<PaginationProvider defaultProductsPerPage={12}>
  <Switch>
    <Route exact path="/" component={LoadableStartPage} />
    <Route path="/search" component={LoadableSearchPage} />
    <Route
      path="/preview"
      render={(props) => (
        <PreviewRoute
          productPage={LoadableProductPage}
          productQuery={ProductPreviewQuery}
          categoryQuery={CategoryPreviewQuery}
          categoryPage={LoadableCategoryPage}
          StartPage={LoadableStartPage}
          {...props}
        />
      )}
    />
    <DynamicRoute
      routeQuery={routeQuery}
      productPage={LoadableProductPage}
      categoryPage={LoadableCategoryPage}
      contentPage={LoadableContentPage}
      notFoundPage={NotFound}
      LoadingPage={LoadingPage}
    />
  </Switch>
</PaginationProvider>;

If you want to set the defaultProductsPerPage for a specific part of the app, e.g. the search page, just wrap that component in its own PaginationProvider and set the defaultProductsPerPage.

If you'd like to allow the end user to change the amount of items shown per page, you can use the setProductsPerPage method from PaginationContext.

// Update number of products displayed per page
setProductsPerPage: (perPage: number) => void;

Example:

Copy
Copied
<PaginationContext.Consumer>
  {({ currentPage, goToPage, perPage, setProductsPerPage }) => (
    <PaginationBehaviour
      currentPage={currentPage}
      goToPage={goToPage}
      perPage={perPage}
      total={category.products.totalResults}
    >
      {(paginationProps) => (
        <Pagination {...paginationProps} setPerPage={setProductsPerPage} />
      )}
    </PaginationBehaviour>
  )}
</PaginationContext.Consumer>

Or, a simple example using React hooks:

Copy
Copied
import { usePagination } from "@jetshop/core/components/Pagination/PaginationProvider";

function UpdatePerPage() {
  const { setProductsPerPage } = usePagination();

  return (
    <button
      onClick={() => {
        setProductsPerPage(48);
      }}
    >
      Set products per page to 48
    </button>
  );
}

Depending on your design, you may want to use a select dropdown to allow the user to set the perPage number.

This perPage value will be persisted throughout the store via a cookie.

Note that the API is limited to 100 products per page, and if you're approaching that many products per page, consider having a look at windowing (with e.g. react-window).

Changing the query param used for pagination

By default, pagination uses the query parameter 'page'. If you'd like to change that, you can pass a prop queryParam to PaginationProvider:

Copy
Copied
<PaginationProvider queryParam="pagenum">
Copyright © Norce 2023. All right reserved.