--- title: Displaying products (CSR) slug: o8ce0239 canonical_url: https://docs.coveo.com/en/o8ce0239/ collection: coveo-for-commerce source_format: adoc --- # Displaying products (CSR) Regardless of the [product discovery solution](https://docs.coveo.com/en/o53d9587/) you're building (search, listings, or recommendations), displaying products to the user is an essential part of the commerce experience. Products can be displayed either as part of a list of products or as a single product on a [product detail page (PDP)](https://docs.coveo.com/en/n8ad7392/). This article explains how to display products and ensure that the correct events, such as [clicks](https://docs.coveo.com/en/o1n92447/) and [product views](https://docs.coveo.com/en/o1n93101/), are logged when a user interacts with a product. ## Displaying lists of products In your commerce application, you'll often need to display a list of products, for instance when showing search results, product recommendations, or [product listing pages](https://docs.coveo.com/en/m1sf3187/). It's important to log [click events](https://docs.coveo.com/en/o1n92447/) when a user interacts with a product in these lists. ### Accessing products in the controller To interact with products and display them, use the controller specific to the product discovery solution you're implementing. * To display products returned from Search, [use the `Search` controller](https://docs.coveo.com/en/o7mc0297/). * To display products on a listing page, [use the `ProductListing` controller](https://docs.coveo.com/en/o78c0545/). * To display products in a recommendation interface, [use the `Recommendations` controller](https://docs.coveo.com/en/o8880413/). Regardless of the controller you use, you can display products by accessing the `state.products` object on the controller. This object contains a list of products (`Product[]`) that you can render to the user. Once you've built one of the controllers listed above and have access to `state.products`, you can pass the products to a component that will render them. > **Note** > > For an example of how to initialize the `Search` controller and access products to pass to the `ProductList` component in the following code snippet, see [Displaying the search page with the Search controller](https://docs.coveo.com/en/p25a2501#displaying-the-search-page-with-the-search-controller). The following example `ProductList` component receives a list of products and renders them: ```jsx import { InteractiveProduct as HeadlessInteractiveProduct, InteractiveProductProps, Product as HeadlessProduct, Cart, } from '@coveo/headless/commerce'; import InteractiveProduct from '../interactive-product/interactive-product'; interface IProductListProps { products: HeadlessProduct[]; <1> controllerBuilder: ( <2> props: InteractiveProductProps ) => HeadlessInteractiveProduct; navigate: (pathName: string) => void; <3> } export default function ProductList(props: IProductListProps) { const {products, controllerBuilder, navigate} = props; if (products.length === 0) { return null; } return ( ); } ``` <1> `ProductList` takes a list of products as input, accessed from the product discovery solution controller. This component is agnostic to the specific controller used. <2> A function that builds the controller for the `InteractiveProduct` component. This function returns an instance of `HeadlessInteractiveProduct`, which logs the correct analytics event when a product is clicked. It can be accessed through the `controller.interactiveProduct` method on the product discovery solution controller. <3> Function to navigate to a specific path in the application. This function will be used to navigate to the PDP when a product is clicked. The implementation details of this function are specific to the application and aren't covered in this example. <4> Loops through the list of products and renders a component for each product. <5> `InteractiveProduct` is a custom component that renders a product and logs the correct analytics event when the product is clicked. For more details on how to implement the `InteractiveProduct` component, [see the next section](#using-the-interactiveproduct-sub-controller). ### Using the `InteractiveProduct` Sub-Controller Every product displayed in a product list—whether on a search page, a product listing page, a recommendations carousel, or an instant products displayed below the search box—must use the [`InteractiveProduct`](https://docs.coveo.com/en/headless/latest/reference/interfaces/Commerce.Search.html#interactiveProduct) sub-controller to ensure that the correct analytics event is logged when the product is clicked. The following code snippet demonstrates how to create an `InteractiveProduct` component that displays a product and uses the `InteractiveProduct` sub-controller to log the `click` event when a product is clicked: ```jsx import { InteractiveProduct as HeadlessInteractiveProduct, Product, } from '@coveo/headless/commerce'; interface IInteractiveProductProps { product: Product; controller: HeadlessInteractiveProduct; navigate: (pathName: string) => void; } export default function InteractiveProduct(props: IInteractiveProductProps) { const {product, controller, navigate} = props; const clickProduct = () => { controller.select(); <1> const productId = product.ec_product_id ?? product.permanentid; navigate(`/product/${productId}`); <2> }; return ( <3>
{product.permanentid}
{product.ec_promo_price}

{product.ec_description}

); } ``` <1> Logs the `click` event using the `InteractiveProduct` controller when the product is clicked. This controller is passed to the component from the `ProductList` component. <2> Navigates to the product detail page when the product is clicked. This function is passed to the component from the `ProductList` component, and the implementation details for this function vary depending on the application. <3> Renders the product information and a button that, when clicked, logs the `click` event and navigates to the product detail page. In the above code sample, the logic to interact with the cart and log events based on user interaction has been omitted to keep the code simple. For more details on implementing this functionality with Headless, see [Managing the cart](https://docs.coveo.com/en/o7v87042/) and the sample application in the [Headless repository](https://github.com/coveo/ui-kit/tree/master/packages/samples/headless-commerce-react/src/components/product-list/product-list.tsx). ## Displaying a product on a product detail page When displaying a product on a [product detail page (PDP)](https://docs.coveo.com/en/n8ad7392/), use the [`ProductView`](https://docs.coveo.com/en/headless/latest/reference/interfaces/Commerce.ProductView.html) controller to ensure that the correct [product view event](https://docs.coveo.com/en/o1n93101/) is logged when a user views a product. When the user navigates to the PDP, product details should be displayed to the user and the `ProductView` controller should be used to log the _product view_ event. ```jsx import { CommerceEngine, Context, buildProductView, } from '@coveo/headless/commerce'; import {useEffect, useRef} from 'react'; import {loadProduct} from '../utils/pdp-utils'; interface IProductDescriptionPageProps { engine: CommerceEngine; contextController: Context; url: string; navigate: (pathName: string) => void; } export default function ProductDescriptionPage(props: IProductDescriptionPageProps) { const {engine, contextController, url, navigate} = props; const productViewEventEmitted = useRef(false); <1> const product = loadProduct(); <2> useEffect(() => { <3> contextController.setView({url}); }, [contextController, url]); useEffect(() => { <4> if (productViewEventEmitted.current || !product) { return; } buildProductView(engine).view(product); productViewEventEmitted.current = true; }, [engine, product]); if (!product) { return null; } return ( <5>

{product.name}

Price: {product.price}

); } ``` <1> Utilize the [`useRef`](https://react.dev/reference/react/useRef) hook to track whether the product view event has been logged for the product displayed on the PDP. This hook ensures that the product view event is only logged once per product per page load. <2> The `loadProduct` function is a placeholder for logic that fetches the product data from a backend service. Implementation details will vary based on your application. <3> Sets the `context` to keep the state of the URL in sync with Headless. For more information on why this is important and how to do it, see [Navigating between pages](https://docs.coveo.com/en/o7v87331/). <4> Logs the product view event when the component mounts, if the event hasn't already been logged and if the product data is available. Use the `buildProductView` function to create an instance of the `ProductView` controller and call the `view` method by passing in the product data to log the product view event. <5> Renders the product information on the PDP. PDP pages typically also include functionality that allows products to be added to the cart. This functionality has been omitted to keep the code simple. For more details on implementing this functionality with Headless, see [Managing the cart](https://docs.coveo.com/en/o7v87042/) and the sample application in the [Headless repository](https://github.com/coveo/ui-kit/tree/master/packages/samples/headless-commerce-react/src/pages/product-description-page.tsx).