Migrate Content Slots from SFRA to Storefront Next

In Storefront Next, React components replace ISML templates. If you're migrating from Salesforce Storefront Reference Architecture (SFRA) and rely on content slots, use this workaround until the planned Embedded Content feature becomes available.

Content Slot Workaround Overview

This workaround lets you manage reusable HTML content, such as shipping policies, disclaimers, or promotional blocks, in Page Designer and embed it across your storefront. It provides:

  • A "Content Slot Page" page type with a single content region.
  • A "Markup Content" component type with a markup attribute for HTML.
  • A React component that renders the markup using the HtmlFragment component.

important: This workaround is temporary. It is replaced by Site-Wide Regions for Content Blocks, which is currently in beta. See Manage Content for Site-Wide Regions in Storefront Next with Content Blocks (Beta).

Step 1: Create the Markup Content Component

Create the markup content in src/components/markup-content/index.tsx.

import { Component } from "@/lib/decorators/component";
import { AttributeDefinition } from "@/lib/decorators/attribute-definition";
import { RegionDefinition } from "@/lib/decorators/region-definition";
import HtmlFragment from "@/components/html-fragment";
import type { ComponentType } from "@/components/region";

@Component("markupContent", {
  name: "Markup Content",
  description: "Displays HTML markup content - use for reusable content blocks",
})
@RegionDefinition([])
export class MarkupContentMetadata {
  @AttributeDefinition({ type: "markup", name: "Markup" })
  markup?: string;
}

interface MarkupContentProps {
  markup?: string;
  component?: ComponentType;
}

export default function MarkupContent({ markup, component }: MarkupContentProps) {
  const content = markup || (component?.data?.markup as string) || "";

  if (!content) return null;

  return <HtmlFragment content={content} />;
}

Step 2: Create the Content Slot Page Type JSON

Create the content slot page. Add the contentSlotPage.json file in cartridges/app_storefrontnext_base/cartridge/experience/pages/ and add this content.

{
  "name": "Content Slot Page",
  "description": "Minimal page type for reusable content - single content region",
  "arch_type": "headless",
  "region_definitions": [
    {
      "id": "content",
      "name": "Content Region",
      "description": "Add Markup Content components here",
      "max_components": 5
    }
  ],
  "supported_aspect_types": [],
  "route": "/content-slot"
}

Step 3: Create a Preview Route for Page Designer

Content slot pages need a preview route to be editable in Page Designer's design mode. Create the _app.content-slot.tsx file in src/routes/_app.content-slot.tsx and add this content.

import { Region } from "@/components/region";
import { fetchPageWithComponentData } from "@/lib/util/pageLoader";
import type { LoaderFunctionArgs } from "react-router";

export function loader(args: LoaderFunctionArgs) {
  return {
    // Replace {content-id} with the ID of the page you create in Page Designer
    page: fetchPageWithComponentData(args, { pageId: "{content-id}" }),
  };
}

export default function ContentSlotPreviewPage({ loaderData }) {
  return (
    <div className="max-w-7xl mx-auto px-4 py-8">
      <Region page={loaderData.page} regionId="content" />
    </div>
  );
}

Step 4: Generate and Deploy Metadata

Use these commands to generate and deploy the metadata.

pnpm cartridge:generate
pnpm push
pnpm cartridge:deploy

Step 5: Set Up a Content Slot in Business Manager

important: To be able to bind content assets to components in Page Designer, turn on a feature switch. In Administration > Global Preferences > Feature Switches, turn on Enable Page Designer Data Binding.

  1. Create a content slot page in Merchant Tools > Content > Page Designer > New Page.
  2. Select Content Slot Page as the page type.
  3. Add Markup Content components to the "content" region.
  4. Configure markup by entering HTML directly or by using data binding to bind to Content Assets.
  5. Assign a page ID (for example, shipping-policy or legal-disclaimer). You reference this ID when fetching the page.

Populate the Content

You can populate content in two ways.

Option 1: Direct HTML entry

Enter HTML directly in the Markup field of a Markup Content component. This approach is best for content that is specific to your Storefront Next storefront.

Option 2: Data binding to content assets

Bind the Markup field to a content asset's HTML attribute. This approach is best for content that must be shared across multiple storefront technologies (SFRA and Storefront Next). Content assets are platform-agnostic, so you can update content once and have it reflected across all storefronts.

Embed Content in Your Storefront

Use fetchPageWithComponentData and the Region component to load and render content slot page data anywhere in your storefront.

import { Region } from "@/components/region";
import { fetchPageWithComponentData } from "@/lib/util/pageLoader";
import type { LoaderFunctionArgs } from "react-router";

export function loader(args: LoaderFunctionArgs) {
  return {
    basket: fetchBasket(args.context),
    // Load content slot page data
    shippingInfo: fetchPageWithComponentData(args, { pageId: "checkout-shipping-info" }),
  };
}

export default function CheckoutPage({ loaderData }) {
  return (
    <div>
      <h1>Checkout</h1>
      {/* Your page content */}

      {/* Embedded content from Page Designer */}
      <aside className="mt-8">
        <Region page={loaderData.shippingInfo} regionId="content" />
      </aside>
    </div>
  );
}

Content updates in Business Manager are reflected immediately without code changes.

Content Slot Workaround Limitations

  • No dedicated APIs for content slot management—uses the same fetchPageWithComponentData as regular pages.
  • No caching or CDN strategies specific to embedded content.
  • Merchants must navigate to Page Designer to edit content—no inline editing where it's embedded.
  • You must create a "page" in Business Manager for content that isn't actually a full page.

When the "Embedded Content" feature ships, migrate your content slot pages to the new system and update component references to the new API.

Quick Reference

Component Checklist

  • @Component decorator with id, name, group, description
  • @AttributeDefinition for each configurable prop
  • @RegionDefinition if component contains nested regions
  • export function fallback() - lightweight loading state
  • export default - main component
  • export const loader - if data loading needed

Page Checklist

  • @PageType decorator with name, description, supportedAspectTypes
  • @RegionDefinition for each region in the page
  • export class [Name]Metadata {} - metadata class
  • <Region> components with matching regionId

Commands

Command Purpose
pnpm build Build the app and generate cartridge.
pnpm cartridge:generate Generate cartridge metadata only.
pnpm push Deploy cartridge to MRT and B2C Commerce.