---
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 (
);
}
```
<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.