Build recommendation interfaces (SSR)

This is for:

Developer
Important

The Headless Commerce SSR utilities are in open beta. Contact your Coveo representative for support in adopting this.

To implement a server-side rendering (SSR) commerce recommendations interface, use the Headless Recommendations controller hooks and a recommendation provider.

Prerequisites

Before you begin building your recommendation interfaces, make sure you:

Define and retrieve the target recommendation controller hooks

When defining your commerce engine, leverage the defineRecommendations functions to define and retrieve the target recommendation hooks.

// lib/commerce-engine-config.ts

import {
  defineRecommendations,
  // ...
} from '@coveo/headless-react/ssr-commerce';

export default {
  // ...
  controllers: {
    popularViewed: defineRecommendations({ 1
      options: {
        slotId: 'd73afbd2-8521-4ee6-a9b8-31f064721e73',
      },
    }),
    popularBought: defineRecommendations({
      options: {
        slotId: 'af4fb7ba-6641-4b67-9cf9-be67e9f30174',
      },
    }),
    viewedTogether: defineRecommendations({
      options: {
        slotId: 'ff5d8804-d398-4dd5-b68c-6a729c66454b',
      },
    }),
    // ...
  },
} satisfies CommerceEngineDefinitionOptions;
1 For each target recommendation controller hook, specify the slotId of the recommendation slot configuration. The slotId is the unique identifier of the slot you want to retrieve recommendations for. This corresponds to the id field of the recommendation slot configuration you’ve created.

Create recommendation components

Next, create recommendation components that use the target recommendation controller hooks. The following is an example implementation for the popularBought strategy.

// components/recommendations/popular-bought.tsx

'use client';

import {usePopularBought} from '@/lib/commerce-engine';
import ProductButtonWithImage from '../product-button-with-image';

export default function PopularBought() {
  const {state, methods} = usePopularBought();

  return (
    <>
      <ul>
        <h3>{state.headline}</h3>
        {state.products.map((product) => ( 1
          <li key={product.ec_product_id}>
            <ProductButtonWithImage methods={methods} product={product} /> 2
          </li>
        ))}
      </ul>
    </>
  );
}
1 Display each recommended product.
2 Use a product button component that uses the interactiveProduct function to correctly log the product click and also handle navigation. See Displaying products.

The recommendation components can then be included in the target pages.

Display recommendations with your recommendation provider

When displaying your recommendation components in a page, wrap them in your recommendation provider. The following is an example of a cart page with a popularBought recommendation component, simplified to focus on the recommendation part.

// app/cart/page.tsx
// runs server-side

// ...
import {
  RecommendationProvider,
  // ...
} from '@/components/providers/providers';
import PopularBought from '@/components/recommendations/popular-bought';
import {
  recommendationEngineDefinition,
  // ...
} from '@/lib/commerce-engine';
import {NextJsNavigatorContext} from '@/lib/navigatorContextProvider';
import {defaultContext} from '@/utils/context';
import {headers} from 'next/headers';

export default async function Search() {
  const navigatorContext = new NextJsNavigatorContext(headers()); 1
  standaloneEngineDefinition.setNavigatorContextProvider(
    () => navigatorContext
  );

  const items = // ... 2


  const recsStaticState = await recommendationEngineDefinition.fetchStaticState(
    {
      controllers: {
        popularBought: {enabled: true}, 3
        popularViewed: {enabled: false},
        viewedTogether: {enabled: false},
        cart: {initialState: {items}},
        context: {
          language: defaultContext.language,
          country: defaultContext.country,
          currency: defaultContext.currency,
          view: {
            url: 'https://sports.barca.group/cart',
          },
        },
      },
    }
  );

  return (
    // ...
      <RecommendationProvider 4
        staticState={recsStaticState}
        navigatorContext={navigatorContext.marshal}
      >
        <PopularBought />
      </RecommendationProvider>
    // ...
  );
}

export const dynamic = 'force-dynamic';
1 Create a navigation context provider.
2 Retrieve cart items.
3 Setting popularBought.enabled to true lets Headless fetch and refresh popularBought recommendations. In the following line, you set popularViewed.enabled to false because you don’t need to fetch and refresh popularViewed recommendations in this component. And again in the following, you set viewedTogether.enabled to false because you don’t need to fetch and refresh viewedTogether recommendations in this component.
4 Wrap your recommendations components with your recommendation provider.

This code sample was abbreviated to focus on recommendations. For the full sample, see the Headless repository.