Installation
This guide covers integrating Alokai CMS into an existing Alokai storefront (Next.js). The integration consists of three parts: the middleware connector, the SDK module, and the React rendering layer.
Prerequisites
- An Alokai storefront (Next.js)
- Access to an Alokai CMS instance (see Dev Docs → Local Development)
@alokai/cms-apipackage available in your registry
Setup
-
Install the API package
Terminal window yarn add @alokai/cms-api -
Add the middleware integration
Create
apps/storefront-middleware/sf-modules/cms-alokai-cms/config.ts:import type { ApiClientExtension, Integration } from "@alokai/connect/middleware";import type { MiddlewareConfig } from "@alokai/cms-api";const {ALOKAI_CMS_BASE_URL = "http://localhost:8787",ALOKAI_CMS_EDITION = "main",ALOKAI_CMS_SPACE,ALOKAI_CMS_DELIVERY_TOKEN,ALOKAI_CMS_PREVIEW_TOKEN,} = process.env;export const config = {configuration: {baseUrl: ALOKAI_CMS_BASE_URL,edition: ALOKAI_CMS_EDITION,space: ALOKAI_CMS_SPACE,deliveryToken: ALOKAI_CMS_DELIVERY_TOKEN!,previewToken: ALOKAI_CMS_PREVIEW_TOKEN,},location: "@alokai/cms-api/server",} satisfies Integration<MiddlewareConfig>;Then register it in
middleware.config.ts:import { config as cmsConfig } from './sf-modules/cms-alokai-cms/config';export default {integrations: {// ... other integrationsalokai-cms: cmsConfig,},}; -
Add the SDK module
Create
apps/storefront-unified-nextjs/sdk/modules/unified-alokai-cms.ts:import { defineSdkModule } from "@vue-storefront/next";import type { UnifiedAlokaiCmsEndpoints } from "storefront-middleware/types";export const unifiedAlokaiCms = defineSdkModule(({ buildModule, config, getRequestHeaders, middlewareModule }) =>buildModule(middlewareModule<UnifiedAlokaiCmsEndpoints>, {apiUrl: `${config.apiUrl}/alokai-cms/unified`,ssrApiUrl: `${config.ssrApiUrl}/alokai-cms/unified`,defaultRequestConfig: {headers: getRequestHeaders(),},}),);Register it in your SDK config:
import { unifiedAlokaiCms } from "./modules/unified-alokai-cms";export const sdk = createSdk(options, ({ buildModule }) => ({// ... other modulesunifiedAlokaiCms: buildModule(unifiedAlokaiCms),})); -
Set environment variables
Add to your middleware
.env:Terminal window ALOKAI_CMS_BASE_URL=https://your-cms.workers.devALOKAI_CMS_SPACE=your-space-idALOKAI_CMS_EDITION=mainALOKAI_CMS_DELIVERY_TOKEN=myorg_tok_...ALOKAI_CMS_PREVIEW_TOKEN=myorg_tok_... -
Add the rendering component
Create
components/cms/page/render-cms-content.tsxand map your CMS componentapi_idvalues to React components:import type { AgnosticCmsComponent } from "@vsf-enterprise/cms-components-utils";import Hero from "@/components/cms/page/hero";import Banner from "@/components/cms/page/banner";// ... import your other componentsconst components: Record<string, ComponentType<any>> = {Hero,Banner,// ... register components by their CMS api_id};export default function RenderCmsContent({ item }: { item: AgnosticCmsComponent | AgnosticCmsComponent[] }) {if (Array.isArray(item)) {return (<>{item.map((c, i) => (<RenderCmsContent key={c.id} item={c} />))}</>);}const { component, ...props } = item;const Component = components[component];if (!Component) return null;return <Component {...props} />;} -
Add the
connectCmsPagewrapper and live previewCreate
sf-modules/cms-alokai-cms/components/connect-cms-page.tsx— this connects CMS pages to Next.js and enables live preview:import type { ComponentType } from "react";import { getSdk } from "@/sdk";import LivePreviewWrapper from "@/sf-modules/cms-alokai-cms/components/live-preview-wrapper";const componentsByPath: Record<string, ComponentType<any>> = {};const appLocaleToCmsLocale: Record<string, string> = { de: "de-DE", en: "en-US" };export default function connectCmsPage<TProps>(PageComponent: ComponentType<TProps>,config: { getCmsPagePath: (props: TProps) => Promise<string> | string },) {async function CmsPage(props: any) {const sdk = await getSdk();const path = await config.getCmsPagePath(props);const params = await props.params;const locale = appLocaleToCmsLocale[params.locale];const searchParams = await props.searchParams;const isPreview = searchParams?.alokai_cms_preview === "true";componentsByPath[path] = PageComponent;const page = await sdk.unifiedAlokaiCms.getPage({ locale, path, ...(isPreview && { preview: true }) }).catch(() => null);async function rerender(pageData: Record<string, unknown>) {"use server";const Component = componentsByPath[path];return <Component {...props} page={pageData} />;}if (isPreview) {return (<><div id="ssr-content"><PageComponent {...props} page={page} /></div><LivePreviewWrapper rerender={rerender} /></>);}return <PageComponent {...props} page={page} />;}return CmsPage;}Create
sf-modules/cms-alokai-cms/components/live-preview-wrapper.tsx:"use client";import { debounce } from "lodash-es";import type { ReactNode } from "react";import { useCallback, useEffect, useState } from "react";export default function LivePreviewWrapper({rerender,}: {rerender: (pageData: Record<string, unknown>) => Promise<ReactNode>;}) {const [previewContent, setPreviewContent] = useState<ReactNode>(null);const debouncedRerender = useCallback(debounce(async (pageData: Record<string, unknown>) => {setPreviewContent(await rerender(pageData));}, 300),[rerender],);useEffect(() => {function handleMessage(event: MessageEvent) {if (event.data?.type === "alokai-cms:preview") {if (event.data.page) debouncedRerender(event.data.page);}}window.addEventListener("message", handleMessage);return () => window.removeEventListener("message", handleMessage);}, [debouncedRerender]);useEffect(() => {const ssr = document.getElementById("ssr-content");if (previewContent && ssr) ssr.style.display = "none";}, [previewContent]);return <>{previewContent}</>;} -
Create a CMS page
Use
connectCmsPagein your route:// app/[locale]/(cms)/[[...slug]]/page.tsximport { notFound } from "next/navigation";import connectCmsPage from "@/sf-modules/cms-alokai-cms/components/connect-cms-page";import RenderCmsContent from "@/sf-modules/cms-alokai-cms/components/render-cms-content";export default connectCmsPage(({ page }) => {if (!page) return notFound();return (<><RenderCmsContent item={page.componentsAboveFold ?? []} /><RenderCmsContent item={page.componentsBelowFold ?? []} /></>);},{async getCmsPagePath(props) {const params = await props.params;return `/${params.slug?.join("/") ?? ""}`;},},);
Environment variables reference
| Variable | Required | Description |
|---|---|---|
ALOKAI_CMS_BASE_URL | Yes | URL of your Alokai CMS worker |
ALOKAI_CMS_SPACE | Yes | Space ID from the CMS dashboard |
ALOKAI_CMS_EDITION | No | Edition name (default: main) |
ALOKAI_CMS_DELIVERY_TOKEN | Yes | Token with delivery permission — serves published content |
ALOKAI_CMS_PREVIEW_TOKEN | No | Token with preview permission — serves draft content + live preview |