mcpField
B
b2c-page-designer

b2c-page-designer

github.com/salesforcecommercecloudDevelopment5789 installsUpdated today
Repository

About b2c-page-designer

A development skill for building Salesforce B2C Commerce Page Designer page types and reusable component types. It covers file structure, schemas, attributes, rendering, and troubleshooting, and directs developers to verify version-specific details against official docs.

Key features

  1. 01Page and component type structure
  2. 02JSON metadata and script patterns
  3. 03Attribute and region configuration
  4. 04Page and component rendering guidance
  5. 05Troubleshooting visibility and schema issues
  6. 06Version-specific documentation checks

Use cases

  1. 01Creating reusable Page Designer components
  2. 02Defining page regions and component attributes
  3. 03Investigating components missing from the editor
  4. 04Checking API and schema details for a platform version

MCP support

The skill recommends using `docs_search` and `docs_read` through MCP to confirm version-specific Page Designer documentation. It does not describe an MCP server.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/salesforcecommercecloud/b2c-developer-tooling --skill b2c-page-designer

After installing, type/b2c-page-designerin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

evals/
trigger-evals.json
references/
ATTRIBUTE-TYPES.md
META-DEFINITIONS.md

Details

From the project README

Page Designer Skill

This skill guides you through creating custom Page Designer page types and component types for Salesforce B2C Commerce.

Scope & grounding

This skill covers B2C Commerce Page Designer (dw.experience package) with version-agnostic patterns, JSON schema structures, and code examples. JSON schemas and Script API signatures shown are illustrative and may drift across platform versions. Before answering questions that name a framework/version, or before emitting code the user will run, confirm against the docs via docs_search/docs_read (MCP) or b2c docs search/b2c docs read (CLI). The B2C Commerce documentation is the canonical grounding source for Script API methods, attribute schema requirements, and version-specific behaviors not covered here.

Canonical docs:

  • dw.experience.PageMgr - rendering pages and regions
  • dw.experience.PageScriptContext - page script context API
  • dw.experience.ComponentScriptContext - component script context API
  • dw.experience.Page - page object methods
  • dw.experience.Component - component object methods
  • dw.experience.Region - region object methods
  • dw.experience.image.Image - image attribute type
  • dw.experience - package overview

Overview

Page Designer allows merchants to create and manage content pages through a visual editor. Developers create:

  1. Page Types - Define page structures with regions
  2. Component Types - Reusable content blocks with configurable attributes

File Structure

Page Designer files are in the cartridge's experience directory:

/my-cartridge
    /cartridge
        /experience
            /pages
                homepage.json           # Page type meta definition
                homepage.js             # Page type script
            /components
                banner.json             # Component type meta definition
                banner.js               # Component type script
        /templates
            /default
                /experience
                    /pages
                        homepage.isml   # Page template
                    /components
                        banner.isml     # Component template

Naming: The .json and .js files must have matching names. Use only alphanumeric or underscore in file names and in any subdirectory names under experience/pages or experience/components.

Component types in subfolders: You can put component meta and script in a subdirectory (e.g. experience/components/assets/). The component type ID is then the path with dots: assets.hero_image_block. The template path under templates/default/ must mirror that path (e.g. templates/default/experience/components/assets/hero_image_block.isml), and the script must call Template('experience/components/assets/hero_image_block') so the path matches. Stored ID is component.{component_type_id}; total length must not exceed 256 characters.

Page Types

Meta Definition (pages/homepage.json)

Illustrative JSON schema; confirm version-specific page type schema requirements with official platform documentation.

{
    "name": "Home Page",
    "description": "Landing page with hero and content regions",
    "region_definitions": [
        {
            "id": "hero",
            "name": "Hero Section",
            "max_components": 1
        },
        {
            "id": "content",
            "name": "Main Content"
        },
        {
            "id": "footer",
            "name": "Footer Section",
            "component_type_exclusions": [
                { "type_id": "video" }
            ]
        }
    ]
}
Page Script (pages/homepage.js)
'use strict';

var Template = require('dw/util/Template');
var HashMap = require('dw/util/HashMap');
var PageRenderHelper = require('*/cartridge/experience/utilities/PageRenderHelper.js');

module.exports.render = function (context) {
    var model = new HashMap();
    var page = context.page;

    model.page = page;
    model.regions = PageRenderHelper.getRegionModelRegistry(page);

    return new Template('experience/pages/homepage').render(model).text;
};
Page Template (templates/default/experience/pages/homepage.isml)

This follows SFRA's region-model pattern: the page script builds pdict.regions with PageRenderHelper.getRegionModelRegistry(page), and each RegionModel.render() call delegates to the platform's PageMgr.renderRegion() with SFRA's render settings.

<isdecorate template="common/layout/page">
    <div class="homepage">
        <div class="hero-region">
            <isprint value="${pdict.regions.hero.render()}" encoding="off"/>
        </div>

        <div class="content-region">
            <isprint value="${pdict.regions.content.render()}" encoding="off"/>
        </div>

        <div class="footer-region">
            <isprint value="${pdict.regions.footer.render()}" encoding="off"/>
        </div>
    </div>
</isdecorate>

Component Types

Meta Definition (components/banner.json)

Illustrative JSON schema; confirm version-specific component type schema requirements with official platform documentation.

{
    "name": "Banner",
    "description": "Promotional banner with image and CTA",
    "group": "content",
    "region_definitions": [],
    "attribute_definition_groups": [
        {
            "id": "image",
            "name": "Image Settings",
            "attribute_definitions": [
                {
                    "id": "image",
                    "name": "Banner Image",
                    "type": "image",
                    "required": true
                },
                {
                    "id": "alt",
                    "name": "Alt Text",
                    "type": "string",
                    "required": true
                }
            ]
        },
        {
            "id": "content",
            "name": "Content",
            "attribute_definitions": [
                {
                    "id": "headline",
                    "name": "Headline",
                    "type": "string",
                    "required": true
                },
                {
                    "id": "body",
                    "name": "Body Text",
                    "type": "markup"
                },
                {
                    "id": "ctaUrl",
                    "name": "CTA Link",
                    "type": "url"
                },
                {
                    "id": "ctaText",
                    "name": "CTA Button Text",
                    "type": "string"
                }
            ]
        },
        {
            "id": "layout",
            "name": "Layout Options",
            "attribute_definitions": [
                {
                    "id": "alignment",
                    "name": "Text Alignment",
                    "type": "enum",
                    "values": ["left", "center", "right"],
                    "default_value": "center"
                },
                {
                    "id": "fullWidth",
                    "name": "Full Width",
                    "type": "boolean",
                    "default_value": false
                }
            ]
        }
    ]
}

Component meta: Always include region_definitions. Use [] when the component has no nested regions (no slots for other components).

Component Script (components/banner.js)
'use strict';

var Template = require('dw/util/Template');
var HashMap = require('dw/util/HashMap');
var URLUtils = require('dw/web/URLUtils');

module.exports.render = function (context) {
    var model = new HashMap();
    var content = context.content;

    // Access merchant-configured attributes
    model.put('image', content.image);        // Image object
    model.put('alt', content.alt);            // String
    model.put('headline', content.headline);  // String
    model.put('body', content.body);          // Markup string
    model.put('ctaUrl', content.ctaUrl);      // URL object
    model.put('ctaText', content.ctaText);    // String
    model.put('alignment', content.alignment || 'center');
    model.put('fullWidth', content.fullWidth);

    return new Template('experience/components/banner').render(model).text;
};

Template path: The path passed to Template(...) must match the template path under templates/default/. If the component lives in a subfolder (e.g. experience/components/assets/hero_image_block), use Template('experience/components/assets/hero_image_block') and place the ISML at templates/default/experience/components/assets/hero_image_block.isml.

Handling colors (string or color picker object): If an attribute can be a hex string or a color picker object { color: "#hex" }, use a small helper so the script works with both:

function getColor(colorAttr) {
    if (!colorAttr) return '';
    if (typeof colorAttr === 'string' && colorAttr.trim()) return colorAttr.trim();
    if (colorAttr.color) return colorAttr.color;
    return '';
}
Component Template (templates/experience/components/banner.isml)
<div class="banner ${pdict.fullWidth ? 'banner--full-width' : ''}"
     style="text-align: ${pdict.alignment}">
    <isif condition="${pdict.image}">
        <img src="${pdict.image.file.absURL}"
             alt="${pdict.alt}"
             class="banner__image"/>
    </isif>

    <div class="banner__content">
        <h2 class="banner__headline">${pdict.headline}</h2>

        <isif condition="${pdict.body}">
            <div class="banner__body">
                <isprint value="${pdict.body}" encoding="off"/>
            </div>
        </isif>

        <isif condition="${pdict.ctaUrl && pdict.ctaText}">
            <a href="${pdict.ctaUrl}" class="banner__cta btn btn-primary">
                ${pdict.ctaText}
            </a>
        </isif>
    </div>
</div>

Attribute Types

Illustrative attribute schema; confirm version-specific attribute_definition syntax with official platform documentation.

TypeDescriptionReturns
stringText inputString
textMulti-line textString
markupRich text editorMarkup string (use encoding="off")
booleanCheckboxBoolean
integerNumber inputInteger
enumSingle select dropdownString or integer (depends on values)
imageImage pickerImage object with file.absURL
fileFile pickerFile object
urlURL pickerURL string
categoryCategory selectorCategory object
productProduct selectorProduct object
pagePage selectorPage object
customJSON object or custom editorObject (or editor-specific)

Enum — critical for component visibility: Use a string array for values: "values": ["left", "center", "right"]. Do not use objects like { "value": "x", "display_value": "X" }; that format can cause the component type to be rejected and not appear in the Page Designer component list. Enum attributes return string when values are strings, integer when values are numeric.

Custom and colors: type: "custom" with e.g. editor_definition.type: "styling.colorPicker" requires a cartridge that provides that editor on the Business Manager site cartridge path. If the component does not show up in the editor, use type: "string" for color attributes (merchant types a hex). In the script, support both: accept a string or an object like { color: "#hex" } (e.g. a small getColor(attr) helper that returns the string).

default_value: Used for storefront rendering only; it is not shown as preselected in the Page Designer visual editor.

Region Definitions

{
    "region_definitions": [
        {
            "id": "main",
            "name": "Main Content",
            "max_components": 10,
            "component_type_exclusions": [
                { "type_id": "heavy-component" }
            ],
            "component_type_inclusions": [
                { "type_id": "text-block" },
                { "type_id": "image-block" }
            ]
        }
    ]
}
PropertyDescription
idUnique region identifier
nameDisplay name in editor
max_componentsMax number of components (optional)
component_type_exclusionsComponents NOT allowed
component_type_inclusionsOnly these components allowed

Rendering Pages

In Controllers
var PageMgr = require('dw/experience/PageMgr');

server.get('Show', function (req, res, next) {
    var page = PageMgr.getPage(req.querystring.cid);

    if (page && page.isVisible()) {
        res.page(page.ID);
    } else {
        res.setStatusCode(404);
        res.render('error/notfound');
    }
    next();
});
In ISML
<isscript>
    var PageMgr = require('dw/experience/PageMgr');
    var page = PageMgr.getPage('homepage-id');
</isscript>

<isif condition="${page && page.isVisible()}">
    <isprint value="${PageMgr.renderPage(page.ID)}" encoding="off"/>
</isif>

Component Groups

Organize components in the editor sidebar:

{
    "name": "Product Card",
    "group": "products"
}

Common groups: content, products, navigation, layout, media

If the component does not appear in Page Designer

  1. Enums: Ensure all enum attributes use "values": ["a", "b"] (string array), not objects with value/display_value.
  2. Custom editors: Replace type: "custom" (e.g. color picker) with type: "string" for the problematic attributes and redeploy; if the component then appears, the issue is likely the custom editor or BM cartridge path.
  3. Naming: File and subdirectory names only alphanumeric or underscore.
  4. region_definitions: Component meta must include region_definitions (use [] if no nested regions).
  5. Template path: Script Template('...') path must match the template path under templates/default/ (including subfolders like experience/components/assets/...).
  6. Logs: In Business Manager, Administration > Site Development > Development Setup, check error logs when opening Page Designer for script or meta errors related to your component type ID.
  7. Code version: Deploy the cartridge to the correct code version; in non-production, meta can be cached for a few seconds—try a code version switch or short wait.

Best Practices

  1. Use dw.util.Template for rendering (NOT dw.template.ISML)
  2. Keep components self-contained - avoid cross-component dependencies
  3. Provide default values for optional attributes (they apply to rendering; not shown as preselected in the editor)
  4. Group related attributes in attribute_definition_groups
  5. Use meaningful IDs - they're used programmatically
  6. Don't change type IDs or attribute types after merchants create content (creates inconsistency with stored data); add a new component type and deprecate the old one if needed
  7. Prefer string arrays for enums and string for colors unless you control the BM cartridge path and custom editors

Detailed Reference

For comprehensive attribute documentation:

Comments

Markdown supported