@coveo/ui-kit-sample-headless-ssr-commerce-nextjs
v2.9.31
Published
Commerce server-side rendering (SSR) with @coveo/headless-react/ssr-commerce and the Next.js App Router
Readme
Headless Commerce SSR with Next.js
Scaffold template:
headless-ssr-commerce-nextjsThis sample is the scaffold template used bynpm create @coveo/uito bootstrap a Coveo Headless commerce project with server-side rendering (SSR) and the Next.js App Router.
A commerce experience built with @coveo/headless-react/ssr-commerce and the Next.js App Router. Product data is fetched and rendered on the server, then hydrated on the client for interactivity. It runs against the public searchuisamples organization with no configuration required.
What it shows
- A commerce
searchpage (search box, facets, sort, pagination) - Product
listingpages served by a single dynamic[category]route - A
cartpage backed by an external-cart abstraction (cookies) - Product
recommendations(popular bought / popular viewed) - Server-side rendering of the initial state with client-side hydration
- Product-click analytics via
interactiveProduct().select()(clicks open the realclickUri)
Technology stack
- Next.js (App Router) + React + TypeScript
- @coveo/headless-react/ssr-commerce: Coveo's SSR commerce utilities
- Playwright + @coveo/platform-mock-api + @mswjs/http-middleware: deterministic end-to-end tests
Prerequisites
- Node.js 20+ (LTS recommended)
- pnpm
Getting started
pnpm install
pnpm dev # start the dev server (http://localhost:3000)
pnpm build # production build
pnpm start # serve the production build (http://localhost:3000)
pnpm e2e # end-to-end tests (Playwright, deterministic mock server)pnpm dev redirects / to /search. No credentials are required — the sample uses the public searchuisamples organization.
Using this sample as an MRE
This sample doubles as a minimal reproducible example for troubleshooting.
- Where to change the configuration: the engine configuration lives in
lib/commerce-engine-config.ts. It callsgetSampleCommerceEngineConfiguration()for the public sample credentials. ReplaceorganizationId,accessToken, and thecontextwith the values that reproduce your issue. - Context (language, country, currency, view): defaults are in
utils/context.ts, and each page passes its ownview.urlwhen callingfetchStaticState(seeapp/**/page.tsx). - Product images: if you point the sample at your own catalog, add your image host to
images.remotePatternsinnext.config.mjs. - Safe to modify:
lib/commerce-engine-config.tsandutils/context.ts(configuration), and any component incomponents/or page inapp/to reproduce a specific UI scenario. - Scaffolding you can usually ignore:
playwright.config.ts,e2e/,mocks/, andproxy.ts(the Next.js proxy/middleware that seeds the navigator context). - Credentials: the
accessTokenandorganizationIdare the publicsearchuisamplessample credentials, safe to share with customers or partners. They are not internal credentials.
Reproducing against a specific version
To reproduce an issue against a specific Coveo UI Kit version, install it after scaffolding:
pnpm add @coveo/headless-react@<version>How the tests stay deterministic
The Playwright tests never call a live Commerce API. During pnpm e2e, Playwright starts a standalone Express server powered by @mswjs/http-middleware (see mocks/mock-server.mjs) and passes its URL to the Next.js server as MOCK_API_URL (see playwright.config.ts). The commerce engine then uses proxyBaseUrl to route both the server-side fetchStaticState calls and the client-side hydration/interactions through that mock server (see lib/commerce-engine-config.ts). The mock server reuses the handlers from @coveo/platform-mock-api.
MOCK_API_URL is deliberately not a NEXT_PUBLIC_* variable: Next.js would bake it into the client bundle at build time, forcing the test suite to build the app itself. Instead the root layout publishes it to the browser at runtime (see lib/mock-api-url.ts), so a single production build works for both. A fixture in e2e/fixtures.ts fails any test whose browser reaches a non-local Commerce API, so this cannot regress unnoticed.
