--- title: Displaying products (Shopify Hydrogen) slug: p24a0093 canonical_url: https://docs.coveo.com/en/p24a0093/ collection: coveo-for-commerce source_format: adoc --- # Displaying products (Shopify Hydrogen) Regardless of the [product discovery solution](https://docs.coveo.com/en/o9cf0524/) you're building (search, listings, or recommendations), displaying products to the user is an essential part of the commerce experience. Products can be displayed as either a part of a product list 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 [storefront](https://docs.coveo.com/en/p33g0410/), 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 users interact with a product. ### Define target controller hooks [Define and use controller hooks](https://docs.coveo.com/en/p1oe0067#define-the-commerce-engine-and-controllers) specific to the product discovery solution you're building. Controller hooks let you interact with products and display them in your [storefront](https://docs.coveo.com/en/p33g0410/). [%header,cols="1,3"] |=== |Context |Function to use to define the controller hook |Search page |[`defineProductList`](https://docs.coveo.com/en/headless-react/latest/reference/functions/SSR_Commerce.index.defineProductList.html) |Listing page |[`defineProductList`](https://docs.coveo.com/en/headless-react/latest/reference/functions/SSR_Commerce.index.defineProductList.html) |Recommendation interface |[`defineRecommendations`](https://docs.coveo.com/en/headless-react/latest/reference/functions/SSR_Commerce.index.defineRecommendations.html) |=== Commerce engine configuration and definition example: ```ts import { defineCommerceEngine } from '@coveo/headless-react/ssr-commerce'; export const engineDefinition = defineCommerceEngine({ // ... controllers: { productList: defineProductList(), homepageRecommendations: defineRecommendations({ options: {slotId: '9a75d3ba-c053-40bf-b881-6d2d3f8472db'}, }), cartRecommendations: defineRecommendations({ options: {slotId: '5a93e231-3b58-4dd2-a00b-667e4fd62c55'}, }), pdpRecommendationsLowerCarousel: defineRecommendations({ options: {slotId: 'a24b0e9c-a8d2-4d4f-be76-5962160504e2'}, }), pdpRecommendationsUpperCarousel: defineRecommendations({ options: {slotId: '05848244-5c01-4846-b280-ff63f5530733'}, }), // ... } // ... }); // ... export const { // ... useProductList, useHomepageRecommendations, useCartRecommendations, usePdpRecommendationsLowerCarousel, usePdpRecommendationsUpperCarousel, // ... } = engineDefinition.controllers; ``` For the full example, see [`coveo-engine.ts`](https://github.com/coveo-labs/barca-sports-hydrogen/blob/main/app/lib/coveo/engine.ts) ### Access products with target hooks With the controller hook for your product discovery solution you can display products by accessing the state `state.products` object on it. This object contains a list of products (`Product[]`) that you can render to the user. Once you've retrieved one of the controller hooks listed above and have access to `state.products`, you can pass the products to a component that will render them. The following example `ProductList` component receives a list of products and renders them: ```tsx import {engineDefinition} from '~/lib/coveo.engine'; import {Link} from '@remix-run/react'; import type {Product} from '@coveo/headless/ssr-commerce'; export function ProductList() { const productList = engineDefinition.controllers.useProductList(); <1> const logClickEvent = (product: Product) => { productList.methods?.interactiveProduct({options: {product}}).select(); }; return ( ); } ``` <1> Retrieve the `useProductList` controller hook. <2> Map over the list of products and render them. <3> When the user selects a product, navigate to the [product detail page (PDP)](https://docs.coveo.com/en/n8ad7392/). <4> Log a click event when the user interacts with a product via the [`InteractiveProduct`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.InteractiveProduct.html) sub-controller. For a complete example of a product list component, see [`ProductList.tsx`](https://github.com/coveo-labs/barca-sports-hydrogen/blob/main/app/components/Search/ProductList.tsx). To display recommendations, you would use the `useHomepageRecommendations`, `useCartRecommendations`, `usePdpRecommendationsLowerCarousel`, or `usePdpRecommendationsUpperCarousel` controller hooks instead of `useProductList`. ```tsx import {useHomepageRecommendations} from '@/lib/coveo.engine'; import {ProductCard} from '../Products/ProductCard'; export function Recommendations() { const homepageRecommendations = useHomepageRecommendations(); return (
{homepageRecommendations.state.products.map((recommendation) => ( <1>
options: {product: recommendation}, }).select } />
))}
); } ``` <1> Display each of the recommended products. <2> Use a product card component that calls the `interactiveProduct` function to log [click events](https://docs.coveo.com/en/o1n92447/) when the user selects the product and to handle navigation. For a complete example of home page recommendations, see [`Recommendations.tsx`](https://github.com/coveo-labs/barca-sports-hydrogen/blob/main/app/components/Homepage/Recommendations.tsx). > **Note** > > Also, to display recommendations, make sure to enable the target recommendation hook when fetching the static state. > See [Displaying recommendations with your recommendation provider](https://docs.coveo.com/en/p36e0272#display-recommendations-with-your-recommendation-provider). ## Displaying a product on a product detail page When displaying a product on [product detail page (PDP)](https://docs.coveo.com/en/n8ad7392/), define and use a hook on the [`ProductView`](https://docs.coveo.com/en/headless-react/latest/reference/functions/SSR_Commerce.index.defineProductView.html) controller to ensure that the correct [product view](https://docs.coveo.com/en/o1n93101/) events are logged when a user views a product. > **Note** > > The following section focuses on Coveo Headless-specific details of a PDP, particularly logging product view events. > > A real Hydrogen project would include additional logic, such as querying Shopify's Storefront API to retrieve product data, particularly fetching product details and selected variants. > > For a complete example of a PDP component, see the [Barca Sports Hydrogen project repository](https://github.com/coveo-labs/barca-sports-hydrogen/blob/main/app/routes/(%24locale).products.%24handle.tsx). Create a product view logger component to include in your product detail pages: ```ts import {useEffect, useCallback} from 'react'; import {useProductView} from '~/lib/coveo.engine'; interface ProductViewLoggerProps { productName: string; productId: string; price: number; } export function ProductViewLogger({ productName, productId, price, }: ProductViewLoggerProps) { const productView = useProductView(); <1> const logProductView = useCallback(() => { <2> if (!productView.methods) return; productView.methods.view({ name: productName, productId, price, }); }, [productView.methods, productName, productId, price]); useEffect(() => { <3> logProductView(); }, [logProductView]); return null; } ``` <1> Retrieve the `useProductView` controller hook, which gives you access to the [`ProductView`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.ProductView.html) controller. <2> Define a function that logs a product view event using the `view` method. Wrap the function in a `useCallback` hook to prevent unnecessary re-creation on re-renders, improving performance. <3> Use the `useEffect` hook to call the `logProductView` function when the component mounts. Once you've created the `ProductViewLogger` component, include it in your product detail page component: ```tsx // app/routes/products.$productId.tsx import {ProductViewLogger} from '~/components/ProductViewLogger'; // ... export default function ProductDetailPage() { return (
productName={productName} productId={productId} price={price} /> {/* Render the rest of the product detail page */}
); } ``` <1> Include the `ProductViewLogger` component in your PDP, passing the product's name, ID, and price.