Displaying products (Shopify Hydrogen)
Displaying products (Shopify Hydrogen)
Regardless of the product discovery solution 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).
This article explains how to display products and ensure that the correct events, such as clicks and product views, are logged when a user interacts with a product.
Displaying lists of products
In your commerce storefront, you’ll often need to display a list of products, for instance when showing search results, product recommendations or product listing pages.
It’s important to log click events when users interact with a product.
Define target controller hooks
Define and use controller hooks specific to the product discovery solution you’re building.
Controller hooks let you interact with products and display them in your storefront.
Context | Function to use to define the controller hook |
---|---|
Search page |
|
Listing page |
|
Recommendation interface |
Commerce engine configuration and definition example:
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
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:
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();
const logClickEvent = (product: Product) => {
productList.methods?.interactiveProduct({options: {product}}).select();
};
return (
<ul>
{productList.state.products.map((product) => (
<li key={product.permanentid}>
<Link
to={`/products/${product.permanentid}`}
onClick={() => logClickEvent(product)}
>
{product.ec_name}
</Link>
</li>
))}
</ul>
);
}
Retrieve the useProductList controller hook. |
|
Map over the list of products and render them. | |
When the user selects a product, navigate to the product detail page (PDP). | |
Log a click event when the user interacts with a product via the InteractiveProduct sub-controller. |
For a complete example of a product list component, see ProductList.tsx
.
To display recommendations, you would use the useHomepageRecommendations
, useCartRecommendations
, usePdpRecommendationsLowerCarousel
, or usePdpRecommendationsUpperCarousel
controller hooks instead of useProductList
.
import {useHomepageRecommendations} from '@/lib/coveo.engine';
import {ProductCard} from '../Products/ProductCard';
export function Recommendations() {
const homepageRecommendations = useHomepageRecommendations();
return (
<div>
{homepageRecommendations.state.products.map((recommendation) => (
<div key={recommendation.permanentid}>
<ProductCard
product={recommendation}
onSelect={
homepageRecommendations.methods?.interactiveProduct({
options: {product: recommendation},
}).select
}
/>
</div>
))}
</div>
);
}
Display each of the recommended products. | |
Use a product card component that calls the interactiveProduct function to log click events when the user selects the product and to handle navigation. |
For a complete example of home page recommendations, see 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. |
Displaying a product on a product detail page
When displaying a product on product detail page (PDP), define and use a hook on the ProductView
controller to ensure that the correct product view 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 |
Create a product view logger component to include in your product detail pages:
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();
const logProductView = useCallback(() => {
if (!productView.methods) return;
productView.methods.view({
name: productName,
productId,
price,
});
}, [productView.methods, productName, productId, price]);
useEffect(() => {
logProductView();
}, [logProductView]);
return null;
}
Retrieve the useProductView controller hook, which gives you access to the ProductView controller. |
|
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. |
|
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:
// app/routes/products.$productId.tsx
import {ProductViewLogger} from '~/components/ProductViewLogger';
// ...
export default function ProductDetailPage() {
return (
<div>
<ProductViewLogger
productName={productName}
productId={productId}
price={price}
/>
{/* Render the rest of the product detail page */}
</div>
);
}
Include the ProductViewLogger component in your PDP, passing the product’s name, ID, and price. |