Build product listing pages (SSR)
Build product listing pages (SSR)
|
The Headless Commerce SSR utilities are in open beta. Contact your Coveo representative for support in adopting this. |
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 using a hook on the
ParameterManager
controller. -
Fetching and displaying listing results using a hook on the
ProductList
controller. -
Wrapping your components with a listing provider to manage the Headless listing state and hydration.
A complete example of a listing page component is available in the sample project in the Headless repository.
Prerequisites
Before you begin building your product listing pages (PLPs), make sure you:
Keeping URL up to date
To ensure that the correct listing page configuration is fetched when navigating between pages, you must keep the URL state updated in Headless.
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
When defining your commerce engine, leverage the defineProductList
function to define and retrieve the target product list hook.
// lib/commerce-engine-config.ts
import {
defineProductList,
// ...
} from '@coveo/headless-react/ssr-commerce';
export default {
// ...
controllers: {
productList: defineProductList(),
// ...
},
// ...
};
Commerce engine definition example:
// lib/commerce-engine.ts
import {defineCommerceEngine} from '@coveo/headless-react/ssr-commerce';
import engineConfig from './commerce-engine-config';
export const engineDefinition = defineCommerceEngine(engineConfig);
// ...
export const {
useProductList,
// ...
} = engineDefinition.controllers;
|
Note
You can use the same |
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.
// app/(listing)/[category]/page.tsx
import ContextDropdown from '@/components/context-dropdown';
import FacetGenerator from '@/components/facets/facet-generator';
import Pagination from '@/components/pagination';
import ParameterManager from '@/components/parameter-manager';
import ProductList from '@/components/product-list';
import { ListingProvider } from '@/components/providers/providers';
import Sort from '@/components/sort';
import StandaloneSearchBox from '@/components/standalone-search-box';
import {listingEngineDefinition} from '@/lib/commerce-engine';
import {NextJsNavigatorContext} from '@/lib/navigatorContextProvider';
import {defaultContext} from '@/utils/context';
import {buildParameterSerializer} from '@coveo/headless-react/ssr-commerce';
import {headers} from 'next/headers';
import {notFound} from 'next/navigation';
export default async function Listing({
params,
searchParams,
}: {
params: {category: string};
searchParams: Promise<URLSearchParams>;
}) {
const {category} = params;
const navigatorContext = new NextJsNavigatorContext(headers());
listingEngineDefinition.setNavigatorContextProvider(() => navigatorContext);
const {deserialize} = buildParameterSerializer();
const parameters = deserialize(await searchParams);
const items = // ...
const staticState = await listingEngineDefinition.fetchStaticState({
controllers: {
cart: {initialState: {items}},
context: {
language: defaultContext.language,
country: defaultContext.country,
currency: defaultContext.currency,
view: {
url: `https://sports.barca.group/browse/promotions/${category}`,
},
},
parameterManager: {initialState: {parameters}},
},
});
return (
<ListingProvider
staticState={staticState}
navigatorContext={navigatorContext.marshal}
>
<ParameterManager url={navigatorContext.location} />
<ContextDropdown useCase="listing" />
<div style={{display: 'flex', flexDirection: 'row'}}>
<div style={{flex: 1}}>
<FacetGenerator />
</div>
<div style={{flex: 2}}>
<Sort />
<ProductList />
<Pagination />
</div>
</div>
</ListingProvider>
);
}
export const dynamic = 'force-dynamic';
Retrieve the search parameters from the URL. | |
Create a navigation context provider. | |
Retrieve cart items. | |
Wrap your components with your listing provider. | |
Use your parameter manager component. | |
Let your users change context. | |
Display the facets generated by the FacetGenerator controller. |
|
Display the sorting options generated by the Sort controller. |
|
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. |
A complete example of how to build a PLP is available in the Headless repository.
Within the sample project, you can find additional components that were omitted in this article, such as the BreadcrumbManager
(displays a summary of the currently active facet values) and Summary
(provides a summary of search results such as the number of results returned).