Skip to main content

CMS integration

A Propeller storefront does not need a CMS: the commerce pages run entirely on the SDK. When you want editorial content (a home page, landing pages, a blog), you connect a headless CMS. The integration keeps rendering and fetching content apart, so you can change CMS without touching your shop.

Checked against propeller-v2-core-ui 0.13.0, propeller-v2-cms-react and propeller-v2-cms-vue 0.1.5 and the Next.js boilerplate of 18 October 2026.

Who owns the page​

Decide first who owns the page URLs. In a commerce-led setup your storefront owns the URLs and pulls content in from the CMS, as the Accelerator boilerplates do. In a CMS-led setup the CMS owns the pages and editors place Propeller components in them as blocks. See Who owns the page for both shapes.

The layers​

LayerWhere it livesRole
Contracts@propeller-commerce/propeller-v2-core-uiFramework-free types for pages, blocks, menus and globals, plus two provider interfaces: CmsAdapter and CmsProvider (see below).
Renderers@propeller-commerce/propeller-v2-cms-react and @propeller-commerce/propeller-v2-cms-vueRender a page's blocks and make a CmsAdapter available to your tree: CmsAdapterProvider, useCms, CmsPageRenderer and CmsBlock. They ship no blocks of their own.
ProviderYour code, or the Next.js boilerplate's lib/cmsFetches content from one specific CMS and converts it to the contract types.
BlocksYour componentsBrand-styled components you register per block type.

The renderer only renders and the provider only fetches. Swap the CMS by swapping the provider: your renderers and blocks stay the same.

The two interfaces​

The core layer defines two provider interfaces. They are different interfaces, not a small and a large version of one:

  • CmsAdapter has three methods: getPage(slug), getMenu(name) and getGlobals(). Its pages have flat blocks (type and data). This is what the renderers' CmsAdapterProvider and useCms take.
  • CmsProvider is the interface the Next.js boilerplate's lib/cms providers implement. Its getPage returns a page with typed blocks (_type plus the block's fields), and it adds getAllPageSlugs, getGlobal, getCategoryBanner, getArticles, getArticle, getAllArticleSlugs and resolveImageUrl, with getMenu optional.

CmsPageRenderer renders a page of either shape: it reads the block type from _type, or from type when there is none.

How rendering works​

The renderer passes each block to a render function you supply in a renderers map, keyed by block type:

// React
import { CmsPageRenderer } from '@propeller-commerce/propeller-v2-cms-react';
import { HeroBlock, RichTextBlock, ProductCarouselBlock } from './cms-blocks';

export function CmsPage({ page }) {
return (
<CmsPageRenderer
page={page}
renderers={{
hero: (block) => <HeroBlock data={block.data} />,
'rich-text': (block) => <RichTextBlock data={block.data} />,
'product-carousel': (block) => <ProductCarouselBlock data={block.data} />,
}}
/>
);
}

CmsBlock renders a single block the same way. Block types without a renderer render nothing, or a debug box when you pass debug. Mount CmsAdapterProvider once at the root so client components can reach the adapter with useCms(). Server-side data fetching calls the adapter directly and passes the page into the view.

Bridge blocks​

A bridge block combines editorial content with commerce data. An editor sets a category and a banner image in the CMS, and the block fetches the category name and its products through the SDK, then renders both together. Bridge blocks are your own components, registered in the same renderers map. They are where CMS content and Propeller data meet.

Writing a provider for another CMS​

Which interface you implement depends on where you plug in:

  • Into the renderers (CmsAdapterProvider and useCms): implement CmsAdapter.
  • Into the Next.js boilerplate's lib/cms: implement CmsProvider, add it to the provider switch in lib/cms and select it with CMS_PROVIDER.

Either way your pages and blocks depend only on the contract types, never on the CMS's own response shape, so supporting another CMS means writing one provider.

With the Accelerator​

The CLI sets up a CMS for Next.js shops. Scaffold with --cms=strapi, --cms=prepr or --cms=contentful, and the matching provider in lib/cms is selected at runtime by CMS_PROVIDER, with a catch-all route for content pages. The settings example lists each provider's settings, and the generated cms/README.md explains the CMS side. Without a CMS (--cms=none), the home page uses a built-in layout and content pages return 404. The commerce pages work the same either way. See Routing for how content routes resolve.

See also​