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_ENVIRONMENT = 'main',ALOKAI_CMS_SPACE,ALOKAI_CMS_DELIVERY_TOKEN,ALOKAI_CMS_PREVIEW_TOKEN,} = process.env;export const config = {configuration: {baseUrl: ALOKAI_CMS_BASE_URL,environment: ALOKAI_CMS_ENVIRONMENT,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_ENVIRONMENT=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_ENVIRONMENT | No | Environment 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 |