Build product listing pages (CSR)

This is for:

Developer

Building product listing pages (PLPs) with Coveo Headless involves two main elements:

  1. Keeping the URL up to date as you navigate between pages using the Context controller

  2. Using the ProductListing controller to fetch product listings and display the results

A complete example of a listing page component is available in the sample project in the Headless repository.

Prerequisite

Make sure that you understand how to build product listing pages (PLPs).

Keep the URL up to date

Keep the URL state updated in Headless to ensure that the correct product listing page (PLP) is fetched when navigating between pages.

The context helps Headless determine which product listing page (PLP) to fetch products for.

For more information, Navigating between pages.

Initializing the ProductListing controller

After initializing your engine, you can pass in this instance to build the controller.

import { commerceEngine } from './Engine';
import { buildProductListing } from '@coveo/headless/commerce';

const productListing = buildProductListing(commerceEngine);

The ProductListing controller has a refresh method you can use to fetch product listings.

Display the listing pages using the ProductListing controller

In addition to ensuring that page navigation correctly updates the url using the Context controller, you must also display the products fetched by the ProductListing controller.

import {
  Cart,
  Search as HeadlessSearch,
  ProductListing,
} from '@coveo/headless/commerce';
import { useState, useEffect } from 'react';
import FacetGenerator from '../../facets/facet-generator/facet-generator';
import Pagination from '../../pagination/pagination';
import ProductList from '../../product-list/product-list';
import Sort from '../../sort/sort';

interface ISearchAndListingInterface {
  searchOrListingController: HeadlessSearch | ProductListing;
  cartController: Cart;
  navigate: (pathName: string) => void;
}

export default function SearchAndListingInterface(
  props: ISearchAndListingInterface
) {
  const {searchOrListingController, cartController, navigate} = props;

  const [searchOrListingState, setSearchOrListingState] = useState(
    searchOrListingController.state
  );

  useEffect(() => { 1
    searchOrListingController.subscribe(() =>
      setSearchOrListingState(searchOrListingController.state)
    );
  }, [searchOrListingController]);

  return (
    <div className="row">
      <div className="column">
        <FacetGenerator 2
          controller={searchOrListingController.facetGenerator()}
        />
      </div>
      <div className="column">
        <Sort controller={searchOrListingController.sort()} /> 3
        <ProductList 4
          products={searchOrListingState.products}
          controllerBuilder={searchOrListingController.interactiveProduct}
          cartController={cartController}
          navigate={navigate}
        ></ProductList>
        <Pagination controller={searchOrListingController.pagination()} /> 5
      </div>
    </div>
  );
}
Note

The previous component can be re-used for both displaying and interacting with results from both search and listing pages.

A complete example of how to build a PLP component is available in the Headless repository.

Within the sample project, you can find additional components that were omitted in this article, such as the BreadcrumbManager (displays a summary of the currently active facet values) and Summary (provides a summary of search results such as the number of results returned).

Display spotlight content

Merchandisers can use spotlight content rules in the Coveo Merchandising Hub (CMH) to insert non-product content, such as banners or editorial assets, into specific positions on product listing pages (PLPs). For information on creating and managing these rules, see Spotlight content rules for product listing pages.

Important

Contact your Coveo representative to enable spotlight content rules in your Coveo organization.

Displaying spotlight content requires Headless version 3.50.0 or later.

To receive spotlight content alongside products, set the enableResults option to true when building the ProductListing controller.

import { commerceEngine } from './Engine';
import { buildProductListing } from '@coveo/headless/commerce';

const productListing = buildProductListing(commerceEngine, {enableResults: true}); 1
1 When enableResults is true, the controller populates state.results with interleaved products and spotlight content instead of state.products.

The state.results array contains items of type Result, a discriminated union that’s either a Product or a SpotlightContent. Use the resultType field to narrow the type and render products and spotlight content differently.

import {ResultType} from '@coveo/headless/commerce';

// ...

{results.map((result) =>
  result.resultType === ResultType.SPOTLIGHT ? ( 1
    <li key={result.id}>
      <InteractiveSpotlightContentCard
        spotlightContent={result}
        controller={searchOrListingController.interactiveSpotlightContent({
          options: {spotlightContent: result},
        })}
      />
    </li>
  ) : (
    <li key={result.permanentid}> 2
      <InteractiveProduct
        product={result}
        controller={searchOrListingController.interactiveProduct({
          options: {product: result},
        })}
        navigate={navigate}
      />
    </li>
  )
)}
1 Render spotlight content items with a dedicated component that uses the InteractiveSpotlightContent sub-controller to log the correct analytics event.
2 Render product items with the InteractiveProduct sub-controller, as described in Displaying products.

For more information on rendering spotlight content and logging click events, see Displaying spotlight content.