Build product listing pages (Shopify Hydrogen)
Build product listing pages (Shopify Hydrogen)
Building product listing pages (PLPs) with Coveo Headless involves three main elements:
-
Keeping the URL up to date with the Headless state as you navigate between pages by using a hook on the
ParameterManager
controller. -
Fetching and displaying listing results by using a hook on the
ProductList
controller. -
Wrapping your components with a listing provider so that you can manage the Headless listing state and hydration.
Prerequisites
Before you begin building your product listing pages (PLPs), make sure you:
Keeping URL up to date
To ensure the correct listing page configuration is fetched when navigating between pages, you must keep the URL state updated in Headless.
The loader
uses fetchStaticState
to sync the URL state with the interface. It’s crucial to pass the correct URL as it’s assigned to context.view
and associated with the listing page configuration to fetch products.
// ...
const staticState = await fetchStaticState({
url: `https://shop.barca.group/plp/${params['*']}`,
context,
parameters,
k: 'listingEngineDefinition',
request,
});
// ...
Must specify the URL that corresponds to the PLP configuration you want to target. |
Managing URL parameters
The Headless React package provides a ParameterManager
controller that lets you manage URL parameters in your commerce application.
For more information, see managing parameters.
Define and retrieve a product list controller hook
While defining your commerce engine, use the defineProductList
function to define and retrieve the target product list hook.
// app/lib/commerce-engine-config.ts
import { defineCommerceEngine } from '@coveo/headless-react/ssr-commerce';
import {
defineProductList,
// ...
} from '@coveo/headless-react/ssr-commerce';
// ...
export default {
// ...
controllers: {
productList: defineProductList(),
// ...
},
//...
};
// ...
export const {
useProductList,
// ...
} = engineDefinition.controllers;
Define the productList controller using the defineProductList function. |
|
You can use the same useProductList hook for both search and listing pages. |
Display listing page results with the listing provider
Render your listing page results using your hook on the ProductList
controller, and wrap your components with your listing provider.
import {type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import {useLoaderData, useParams, type MetaFunction} from '@remix-run/react';
import {
ClientSideNavigatorContextProvider,
ServerSideNavigatorContextProvider,
} from '~/lib/navigator.provider';
import {
engineDefinition,
fetchStaticState,
type ListingStaticState,
} from '~/lib/coveo.engine';
import {ListingProvider} from '~/components/Search/Context';
import {buildParameterSerializer} from '@coveo/headless-react/ssr-commerce';
import {useEffect, useState} from 'react';
import ParameterManager from '~/components/ParameterManager';
// ...
export async function loader({context, params, request}: LoaderFunctionArgs) {
const {listingEngineDefinition} = engineDefinition;
const url = new URL(request.url);
const {deserialize} = buildParameterSerializer();
const parameters = deserialize(url.searchParams);
listingEngineDefinition.setNavigatorContextProvider(
() => new ServerSideNavigatorContextProvider(request),
);
const staticState = await fetchStaticState({
url: `https://shop.barca.group/plp/${params['*']}`,
context,
parameters,
k: 'listingEngineDefinition',
request,
});
return {staticState, url};
}
export default function PLP() {
const {staticState, url} = useLoaderData<typeof loader>();
const [currentUrl, setCurrentUrl] = useState(url);
useEffect(() => {
setCurrentUrl(window.location.href);
}, []);
// ...
return (
<ListingProvider
navigatorContext={new ClientSideNavigatorContextProvider()}
staticState={staticState as ListingStaticState}
>
<ParameterManager url={currentUrl} />
<section>
<div>
<div>
<Sorts />
</div>
<div ref={facetsContainer}>
<Facets numFacetsInLine={numFacetsInline} />
</div>
</div>
<Breadcrumbs />
</section>
<section>
<ProductList />
<PaginationFooter />
</section>
</ListingProvider>
);
}
Deserialize the URL parameters using the deserialize function returned by the buildParameterSerializer function. |
|
Create a navigation context provider. | |
Fetch the static state for the listing page with the deserialized parameters as the initial value. | |
Wrap your components in a listing provider. | |
Use your ParameterManager component. |
|
Display the sorting options generated by the Sort controller. |
|
Display the facets generated by the FacetGenerator controller. |
|
Displays a summary of the currently active facet values via the BreadcrumbManager . |
|
Render the list of products returned by the query.
The ProductList component relies on the useProductList hook to fetch and display the products. |
|
Display the pagination generated by the Pagination controller. |
For the complete example, see the Barca Sports repository.