@coveo/headless-react
    Preparing search index...

    Getting started with Commerce SSR

    This guide walks through setting up a server-side rendered commerce storefront using @coveo/headless-react/ssr-commerce.

    Note

    Commerce SSR utilities are currently in Open Beta.

    Before getting started, make sure that:

    • You have a working knowledge of React and a React-based framework such as Next.js.
    • You're familiar with Coveo Headless engines and controllers. You can refer to the Headless usage documentation for an introduction.
    • You have Node.js version 20 or later installed.

    Use npm to install the package along with its peer dependencies:

    npm install @coveo/headless-react @coveo/headless react react-dom
    
    Note

    @coveo/headless-react requires React 18 or later. If you use TypeScript, note that Headless doesn't support the classic or node10/node moduleResolution options.

    The SSR utilities revolve around three core concepts:

    1. Engine definition — A configuration object that specifies the engine settings and the set of controllers your application needs. It provides methods to fetch the static state and to generate the hydrated state.

    2. Static state — The initial state of the application, generated on the server. It contains the data needed for the first render (for example, initial product listings) without interactivity.

    3. Hydrated state — The state after client-side hydration. It contains live Headless controllers with methods you can call to add interactivity, as well as the Headless engine instance itself.

    Create and export an engine definition in a shared file. Include the controllers and the commerce engine configuration:

    // lib/commerce-engine.ts

    import {
    defineCommerceEngine,
    defineSummary,
    defineProductList,
    defineCart,
    getSampleCommerceEngineConfiguration,
    } from '@coveo/headless-react/ssr-commerce';

    export const engineDefinition = defineCommerceEngine({
    configuration: {
    ...getSampleCommerceEngineConfiguration(),
    },
    controllers: {
    summary: defineSummary(),
    productList: defineProductList(),
    cart: defineCart(),
    },
    });

    export const {
    useSummary,
    useProductList,
    useCart,
    } = engineDefinition.controllers;

    The commerce engine definition provides four solution-type definitions that you can use depending on the page type:

    • engineDefinition.listingEngineDefinition — For product listing pages.
    • engineDefinition.searchEngineDefinition — For search result pages.
    • engineDefinition.standaloneEngineDefinition — For standalone components (for example, a standalone search box).
    • engineDefinition.recommendationEngineDefinition — For recommendation widgets.

    Each controller defined in the engine definition automatically generates a corresponding React hook. The hook is named after the controller key, capitalized and prefixed with use (for example, productListuseProductList).

    Important

    If you're using Next.js with the App Router, any file which uses these hooks must begin with the 'use client' directive.

    'use client';

    import { useProductList, useCart } from '../lib/commerce-engine';

    export function ProductList() {
    const { state, methods } = useProductList();
    const { methods: cartMethods } = useCart();

    return (
    <ul>
    {state.products.map((product) => (
    <li key={product.ec_product_id}>
    <span>{product.ec_name}</span>
    <button onClick={() => cartMethods?.updateItemQuantity({
    productId: product.ec_product_id!,
    name: product.ec_name!,
    price: product.ec_price!,
    quantity: 1,
    })}>
    Add to cart
    </button>
    </li>
    ))}
    </ul>
    );
    }
    Note

    The methods property is undefined when rendering with the StaticStateProvider (that is, on the server). Always use optional chaining (cartMethods?.updateItemQuantity(...)) or guard against undefined before calling methods.

    The commerce engine definition provides buildProviderWithDefinition, a utility that creates a provider component handling the static-to-hydrated state transition for you:

    import { buildProviderWithDefinition } from '@coveo/headless-react/ssr-commerce';
    import { engineDefinition } from '../lib/commerce-engine';

    export const ListingProvider = buildProviderWithDefinition(
    engineDefinition.listingEngineDefinition
    );

    You can then use this provider in your server-rendered page to wrap your components:

    import { engineDefinition } from '../lib/commerce-engine';
    import { ListingProvider } from '../components/ListingProvider';
    import { ProductList } from '../components/ProductList';

    const { listingEngineDefinition } = engineDefinition;

    export default async function ListingPage() {
    const staticState = await listingEngineDefinition.fetchStaticState();

    return (
    <ListingProvider staticState={staticState}>
    <ProductList />
    </ListingProvider>
    );
    }