Skip to main content

CMS Integration

Beta

The Storefront SDK is in beta. Content in this section may be updated as the SDK evolves.

CMS integration works differently depending on which approach you use:

ApproachCMS role
Pattern A (Accelerator)The Accelerator owns routing. The CMS handles content pages via a catch-all route. This is what the adapter pattern below describes.
Pattern B (CMS-led)The CMS owns all pages, URLs and routing. You import SDK Services and UI Components into your CMS's frontend framework. The adapter pattern is not needed because your CMS handles content directly.
Pattern C (SDK-only)No CMS involvement. You handle content in your own code.

The rest of this page describes how CMS integration works in the Accelerator (Pattern A). Strapi is the default CMS. Partners using a different CMS can write their own adapter.

The adapter pattern

The Accelerator uses a CMS adapter pattern so that page templates and CMS block components never depend on a specific CMS. Three packages make this work:

PackagePurpose
@propeller/cms-coreUnified TypeScript interfaces (CmsProvider, CmsPage, CmsBlock, CmsImage). Accelerator page templates and CMS block components depend on these types.
@propeller/cms-strapiStrapi adapter with normalizers that transform Strapi responses into the unified types. This is the default.

Partners pick their CMS by changing a single file:

// lib/cms/index.ts
import { createStrapiProvider } from '@propeller/cms-strapi';

export const cmsProvider = createStrapiProvider({
apiUrl: '...',
apiToken: '...',
});

To switch to a different CMS, only this file changes. No components, routing config or templates need to change.

Writing a custom adapter

If your project uses a CMS other than Strapi, you implement the interfaces defined in @propeller/cms-core. Your adapter normalizes your CMS's response format into the unified types that the Accelerator's page templates and CMS block components expect.

CMS block components

CMS block components live in the Accelerator, not in the UI Components library. They are distinct from the SDK-only UI components described in Layer 2.

CMS block components receive data through the unified CmsPage and CmsBlock types. A HeroBanner block component does not know which CMS is behind the adapter. It receives a block object with fields like image, title and categoryId, and renders them.

This separation means you can evaluate or switch CMS platforms without rewriting your Accelerator's CMS block components.

Bridge blocks

Bridge blocks are the key pattern for combining CMS content with commerce data. A bridge block is a component that:

  1. Receives editorial data from the CMS (an image, a category ID, a text block)
  2. Fetches commerce data from the Propeller SDK (category name, product list, pricing)
  3. Renders both together

For example, an editor sets categoryId: 17 and uploads a banner image in the CMS. The bridge block component fetches the category name and slug from the Propeller API, then renders the banner image alongside the category title with a link to the category page.

Dynamic block rendering

CMS pages are built from blocks (called Dynamic Zones in Strapi). The Accelerator includes a DynamicBlockRenderer that maps each block type to a component:

Block typeComponent
hero-bannerHeroBanner
rich-textRichText
category-bannerCategoryBanner
product-carouselProductCarousel

When you create custom block types in your CMS, you add the mapping in components/cms/DynamicBlockRenderer.tsx.

Site-wide CMS data

The header, footer and navigation links are managed in the CMS under a global configuration (a "single type" in Strapi). The root layout.tsx calls getGlobal() on every request and makes the result available to all pages through a GlobalProvider.

When a CMS is connected, global data (logo, navigation, footer) is fetched for every page, including commerce pages. When no CMS is configured, getGlobal() returns null and the application renders without CMS data.

See also