Skip to main content

<CmsPageRenderer>

Renders a CmsPage's block list. Each block is dispatched through the renderers map by its block.type discriminator.

import { CmsPageRenderer } from 'propeller-v2-cms-react';

Props

PropTypeRequiredNotes
pageCmsPageyesThe page object returned by adapter.getPage(slug).
renderersRecord<string, CmsBlockRenderer>yesMap keyed by block.type.
fallback(block) => ReactNodenoRenders blocks whose type is not in renderers. Default: null.
classNamestringnoApplied to the outer wrapper <div>.
type CmsBlockRenderer = (block: CmsBlock) => ReactNode;

Example

<CmsPageRenderer
page={page}
renderers={{
hero: (b) => <HeroBlock data={b.data} />,
text: (b) => <TextBlock html={b.data.html} />,
image: (b) => <ImageBlock src={b.data.src} alt={b.data.alt} />,
productCarousel: (b) => <ProductCarouselBlock skus={b.data.skus} />,
}}
fallback={(b) => {
if (process.env.NODE_ENV !== 'production') {
return <div className="cms-unknown">Unknown block type: {b.type}</div>;
}
return null;
}}
/>

What it does NOT do

  • No client-side fetching. The page must already be loaded. Server-fetch on the route and pass it in as a prop. Client-side fetching is the consumer's call — see useCms().
  • No block-level loading or error states. Blocks are synchronous renders. If a block needs to fetch its own data (a product carousel hitting the commerce API), that's the block component's responsibility.
  • No layout opinions. Blocks render in document order inside a single wrapper <div>. Apply your own CSS / layout primitives in the block components themselves.

Wrapping behaviour

The output is a single <div> with the optional className, then one child per block. The block components themselves render whatever fragments / wrappers they want. If you need block-level wrappers (rails, sections), build them into each block component — the page renderer is intentionally thin.