--- title: Headless commerce usage (server-side rendering) slug: obif0156 canonical_url: https://docs.coveo.com/en/obif0156/ collection: coveo-for-commerce source_format: adoc --- # Headless commerce usage (server-side rendering) > **Important** > > The Headless Commerce SSR utilities are in open beta. > Contact your Coveo representative for support in adopting this. [server-side rendering (SSR)](https://docs.coveo.com/en/p2af0263/) is a technique that allows web applications to be rendered on the server before sending them to the client. This approach is particularly useful when developing with the [Coveo Headless](https://docs.coveo.com/en/lcdf0493/) framework because it enables faster initial loading times and better SEO. By rendering the HTML on the server, the client can receive a fully formed page which is ready to be displayed as soon as it's received. The [`@coveo/headless-react`](https://www.npmjs.com/package/@coveo/headless-react) package includes utilities for [React](https://react.dev/) which are compatible with [Next.js](https://nextjs.org/) in the `@coveo/headless-react/ssr-commerce` sub-package. These utilities enable SSR with the [Headless](https://docs.coveo.com/en/lcdf0493/) framework. This article introduces the different pieces required to put together a Headless SSR commerce app using a Next.js example. It assumes you already understand how to [set up a Next.js project](https://nextjs.org/docs/app/getting-started/installation). > **Note** > > While the core concepts discussed in this article apply to SSR with React, the code samples are specific to Next.js. > > Depending on your use case, you might find other relevant samples in the [project repository](https://github.com/coveo/ui-kit/tree/main/samples/headless-ssr). At a high level, here's the architecture involved:  > **Tip** > > To focus on relevant concepts and ease comprehension, the code samples in this article are simplified. > For a complete example, see the [headless-ssr-commerce-nextjs](https://github.com/coveo/ui-kit/tree/main/samples/headless-ssr/commerce-nextjs) sample. > Links to different parts of the sample will be provided throughout this article. ## Install the library Use npm to install [Headless React Utils for SSR](https://www.npmjs.com/package/@coveo/headless-react): ```bash npm install @coveo/headless-react ``` ## Define the commerce engine and controllers The Headless commerce _engine_ manages the state of the [product discovery solution](https://docs.coveo.com/en/o9cf0524/) interface and communicates with the [Coveo Platform](https://docs.coveo.com/en/186/). Rather than _initializing_ the commerce engine in the client, you must _define_ a commerce engine. This is because the commerce engine definition is a singleton that must be shared between the server and the client. A Headless _controller_ is an abstraction that simplifies the implementation of a specific Coveo-powered UI feature or component. In other words, controllers provide programming interfaces for interacting with the Headless engine's state. When defining your engine, specify the controllers you want to use in your commerce site. You'll then be able to retrieve controller hooks to interact with the engine in your components. The following is an example commerce engine configuration file. ```tsx // lib/commerce-engine-config.ts import {<1> CommerceEngineDefinitionOptions, defineContext, defineSummary, defineRecommendations, // ... } from '@coveo/headless-react/ssr-commerce'; export default { configuration: { organizationId: 'searchuisamples', <2> accessToken: 'xx564559b1-0045-48e1-953c-3addd1ee4457', <3> context: { <4> language: 'en', country: 'US', currency: 'USD', view: { url: 'https://sports.barca.group', }, }, analytics: { <5> trackingId: 'sports-ui-samples', }, cart: { <6> items: [ { // ... }, ], }, }, controllers: { <7> context: defineContext(), summary: defineSummary(), popularViewed: defineRecommendations({ <8> options: { slotId: 'd73afbd2-8521-4ee6-a9b8-31f064721e73', }, }), // ... }, } satisfies CommerceEngineDefinitionOptions; ``` <1> Import the necessary controller definers. <2> The organization ID is the [unique identifier of your Coveo organization](https://docs.coveo.com/en/n1ce5273/). <3> The access token is a [search token](https://docs.coveo.com/en/o8ld0051#filtering-content-using-search-tokens) or an [API key](https://docs.coveo.com/en/o8ld0051#api-keys) that grants the **Allowed** [access level](https://docs.coveo.com/en/2818/) on the [**Execute Queries**](https://docs.coveo.com/en/1707#execute-queries-domain) [domain](https://docs.coveo.com/en/2819/) and the **Push** [access level](https://docs.coveo.com/en/2818/) on the [**Analytics Data**](https://docs.coveo.com/en/1707#administrate-domain) [domain](https://docs.coveo.com/en/2819/) in the target [organization](https://docs.coveo.com/en/185/). To improve security, client-side specification of user IDs isn't supported by Event Protocol. To specify user IDs, enforce them through [search tokens](https://docs.coveo.com/en/56/). <4> The [`context`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.CommerceEngineConfiguration.html#context) object contains information about the user's context, such as the currency, country, language, and the URL. Every time the user navigates between pages, you must update this context. <5> Via the [`analytics`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.CommerceEngineConfiguration.html#analytics) object, specify the [tracking ID](https://docs.coveo.com/en/o8rb0139/). <6> Pass in the initial state of the cart by specifying the [`CartInitialState`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.CommerceEngineConfiguration.html#cart) object. <7> Specify the controllers to define as part of the commerce engine definition. <8> Certain controller definers require additional options. For example, here you must specify which recommendation slot to use. Use your engine configuration to define the commerce engine, as in the following example. ```tsx // lib/commerce-engine.ts import {defineCommerceEngine} from '@coveo/headless-react/ssr-commerce'; import engineConfig from './commerce-engine-config'; export const engineDefinition = defineCommerceEngine(engineConfig); <1> export const { listingEngineDefinition, <2> searchEngineDefinition, recommendationEngineDefinition, standaloneEngineDefinition, useEngine, <3> } = engineDefinition; export const { useContext, useSummary, usePopularViewed, // ... } = engineDefinition.controllers; <4> ``` <1> Define the commerce engine using the target commerce engine configuration. <2> Engines for different use cases in your commerce site. More on that later. <3> `useEngine` is a hook that lets you access the core commerce engine. More on that later. <4> Retrieve the controller hooks created based on the controllers definitions you provided in the commerce engine configuration. For complete code samples, see [commerce-engine-config.ts](https://github.com/coveo/ui-kit/tree/main/samples/headless-ssr/commerce-nextjs/lib/commerce-engine-config.ts) and [commerce-engine.ts](https://github.com/coveo/ui-kit/tree/main/samples/headless-ssr/commerce-nextjs/lib/commerce-engine.ts). ## Create a navigation context provider For SSR, you must create a navigation context provider that will wrap relevant navigation context information (such as the URL and [`clientId`](https://docs.coveo.com/en/masb0234/)) and make it available to the server. ### Capture the navigation context with middleware Create a [Next.js middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) to handle the URL and client ID. For simplicity, this example always generates a new [`clientId`](https://docs.coveo.com/en/masb0234/) for each request. In a real implementation, you would likely use a more sophisticated approach to generate and manage client IDs with browser cookies or local storage. See [Managing client IDs with server-side rendering](https://docs.coveo.com/en/oa890580/). ```tsx // middleware.ts <1> import {NextRequest, NextResponse} from 'next/server'; export default function middleware(request: NextRequest) { const response = NextResponse.next(); const requestHeaders = new Headers(request.headers); const uuid = crypto.randomUUID(); <2> requestHeaders.set('x-coveo-client-id', uuid); <3> response.headers.set('x-coveo-client-id', uuid); response.headers.set('x-href', request.nextUrl.href); <4> return response; } ``` <1> [Next.js](https://nextjs.org/docs/app/building-your-application/routing/middleware) treats `middleware.ts` as a middleware file. <2> Generate a unique `clientId` for the request and response. <3> Set the `clientId` in both the request and response headers so that it can be used by the commerce engine server-side and client-side across sessions. <4> Make sure to pass along the `x-href` header, which is the URL of the request, in the response. More on that below. ### Create the navigation context object Then, in your Next.js app, create a navigation context provider that will use the headers from the middleware to create a navigation context object. ```tsx // lib/navigatorContextProvider.ts import {NavigatorContext} from '@coveo/headless-react/ssr-commerce'; import type {ReadonlyHeaders} from 'next/dist/server/web/spec-extension/adapters/headers'; export class NextJsNavigatorContext implements NavigatorContext { constructor(private headers: ReadonlyHeaders) {} <1> get referrer() { return this.headers.get('referer') || this.headers.get('referrer'); <2> } get userAgent() { return this.headers.get('user-agent'); } get location() { return this.headers.get('x-href'); } get clientId() { const clientId = this.headers.get('x-coveo-client-id'); return clientId!; } get marshal(): NavigatorContext { <3> return { clientId: this.clientId, location: this.location, referrer: this.referrer, userAgent: this.userAgent, }; } } ``` <1> Takes in the read-only [headers](https://nextjs.org/docs/app/api-reference/functions/headers) from a Next.js request, which let you access request-specific data. <2> Some browsers use `referer` while others may use `referrer`. <3> Marshals the navigation context into a format that can be used by Coveo's Headless library. See [`NavigatorContext`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.NavigatorContext.html) for more information. You'll use your context provider later in the different [pages](#assemble-pages) of your site (more details on creating pages later). See [navigatorContextProvider.ts](https://github.com/coveo/ui-kit/tree/main/samples/headless-ssr/commerce-nextjs/lib/navigatorContextProvider.ts) and [page.tsx](https://github.com/coveo/ui-kit/tree/main/samples/headless-ssr/commerce-nextjs/app/search/page.tsx) for complete code samples. ## Create providers Providers take care of displaying your page with the static state, and then [hydrating](https://docs.coveo.com/en/p2ae0404/) the state and displaying the page with the hydrated state. They're required for your controller hooks to function.  Coveo Headless React exposes the https://docs.coveo.com/en/headless-react/latest/reference/functions/SSR_Commerce.index.buildProviderWithDefinition.html[`buildProviderWithDefinition`^] utility function that creates a provider for a given engine definition. It simplifies the process of building providers for your components. ```tsx // components/providers/providers.tsx 'use client'; import { listingEngineDefinition, recommendationEngineDefinition, searchEngineDefinition, standaloneEngineDefinition, } from '@/lib/commerce-engine'; import {buildProviderWithDefinition} from '@coveo/headless-react/ssr-commerce'; export const ListingProvider = buildProviderWithDefinition( <1> listingEngineDefinition ); export const SearchProvider = buildProviderWithDefinition( <2> searchEngineDefinition ); export const RecommendationProvider = buildProviderWithDefinition( <3> recommendationEngineDefinition ); export const StandaloneProvider = buildProviderWithDefinition( <4> standaloneEngineDefinition ); ``` <1> For listing pages to provide context for listing-specific hooks. <2> For search pages to provide context for search-specific hooks. <3> For recommendation slots, regardless of the slot location (for example, home page, product detail page, or some other page.) <4> For components that don't require triggering a search or product fetch (such as a cart page or standalone search box). ## Create components In a Headless SSR commerce implementation, contrary to a non-SSR one, you generally don't interact with the controllers or engine directly in your components. We rather recommend leveraging the controller and engine hooks retrieved [earlier](#define-the-commerce-engine-and-controllers). For example, here's how to implement a `ShowMore` component. ```tsx // components/show-more.tsx 'use client'; <1> import {usePagination, useSummary} from '@/lib/commerce-engine'; <2> export default function ShowMore() { const pagination = usePagination(); const summary = useSummary(); const isDisabled = () => { <3> return ( !pagination.methods || summary.state?.lastProduct === summary.state?.totalNumberOfProducts ); }; return ( <>