b2c-page-designer
This skill helps create custom Page Designer page types and component types for Salesforce B2C Commerce.
Community author
- Agents
- Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode, claude.ai and API
- License
- Apache-2.0 (permissive)
- Pin
- be57627pinned Oct 9, 2026
- Context cost
- about 3,900 tokenswhen the skill loads
- GitHub stars
- 56on salesforcecommercecloud/b2c-developer-tooling
- Scripts
- None found
What it does
“Define Page Designer page types and component types with regions, attributes, and rendering scripts. Use this skill whenever the user needs to create a new page or component type, configure regions with allowed component constraints, define attribute_definition_groups, debug why a component does not appear in the editor, or fix enum/color attribute pitfalls. Also use when building visual merchandising experiences -- even if they just say 'my component is missing from PD' or 'add a hero banner component'.”
Generated summary from the skill's own text
This skill helps create custom Page Designer page types and component types for Salesforce B2C Commerce. It covers defining page structures with regions, building reusable component blocks with configurable attributes, rendering scripts, and templates. Use it to debug missing components, fix enum and color attribute issues, and build visual merchandising experiences.
When to use it
- Creating new page or component types for Page Designer
- Configuring regions with component constraints and exclusions
- Defining attribute groups and handling special attribute types
- Troubleshooting why components don't appear in the editor
When not to
- Managing content after page types are created by merchants
- Implementing storefront features outside Page Designer
- Working with B2C Commerce packages other than dw.experience
Install
These commands install from the publisher's repository and need no account.
skills CLI
npx skills add salesforcecommercecloud/b2c-developer-tooling --skill b2c-page-designerInstalls from the repository's default branch, which may have changed since the commit we checked.
GitHub CLI 2.90 or later
gh skill install salesforcecommercecloud/b2c-developer-tooling b2c-page-designer@be57627a499d2b1c5992161d0afe0a85bd79e00aInstalls the commit we checked (be57627).
Copy by hand
Download the folder at be57627 and put it in .claude/skills/b2c-page-designer for Claude Code and in .agents/skills/b2c-page-designer for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode.
Copy for your agent
A prompt for any coding agent. It installs this skill from GitHub at the pinned commit, copies the files as they are, and runs nothing unless you ask.
Prompt
Install the agent skill "b2c-page-designer" by SalesforceCommerceCloud. Source, pinned to commit be57627a499d2b1c5992161d0afe0a85bd79e00a: https://github.com/salesforcecommercecloud/b2c-developer-tooling/tree/be57627a499d2b1c5992161d0afe0a85bd79e00a/skills/b2c/skills/b2c-page-designer 1. Download that folder at exactly this commit. Do not use the default branch. 2. Copy every file unchanged, keeping the folder structure, into .claude/skills/b2c-page-designer/ for Claude Code and .agents/skills/b2c-page-designer/ for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode. Create the folders if they do not exist. 3. Do not edit the files, and do not run any script in the folder unless I ask you to. 4. When you are done, list the files you copied and where you put them. Listing and checks: https://www.agentnative.dev/skills/salesforcecommercecloud/b2c-page-designer
SKILL.md at the pin
---
name: b2c-page-designer
description: Define Page Designer page types and component types with regions, attributes, and rendering scripts. Use this skill whenever the user needs to create a new page or component type, configure regions with allowed component constraints, define attribute_definition_groups, debug why a component does not appear in the editor, or fix enum/color attribute pitfalls. Also use when building visual merchandising experiences -- even if they just say 'my component is missing from PD' or 'add a hero banner component'.
---
# 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.
```json
{
"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)
```javascript
'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.
```html
<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.
```json
{
"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)
```javascript
'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:
```javascript
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)
```html
<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.
| Type | Description | Returns |
|------|-------------|---------|
| `string` | Text input | String |
| `text` | Multi-line text | String |
| `markup` | Rich text editor | Markup string (use `encoding="off"`) |
| `boolean` | Checkbox | Boolean |
| `integer` | Number input | Integer |
| `enum` | Single select dropdown | String or integer (depends on values) |
| `image` | Image picker | Image object with `file.absURL` |
| `file` | File picker | File object |
| `url` | URL picker | URL string |
| `category` | Category selector | Category object |
| `product` | Product selector | Product object |
| `page` | Page selector | Page object |
| `custom` | JSON object or custom editor | Object (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
```json
{
"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" }
]
}
]
}
```
| Property | Description |
|----------|-------------|
| `id` | Unique region identifier |
| `name` | Display name in editor |
| `max_components` | Max number of components (optional) |
| `component_type_exclusions` | Components NOT allowed |
| `component_type_inclusions` | Only these components allowed |
## Rendering Pages
### In Controllers
```javascript
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
```html
<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:
```json
{
"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:
- [Meta Definitions Reference](references/META-DEFINITIONS.md) - Full JSON schema
- [Attribute Types Reference](references/ATTRIBUTE-TYPES.md) - All attribute types with examples
From salesforcecommercecloud/b2c-developer-tooling at be57627, shown as plain text under its license: Apache-2.0 (permissive). The license text is in license.txt.
View SKILL.md on GitHub at be57627Files
- SKILL.md16 KB · f40bc64c0a8f
- evals/
- trigger-evals.json2.5 KB · 509965c2d7a0
- references/
- ATTRIBUTE-TYPES.md8.3 KB · e40b349b6d1c
- META-DEFINITIONS.md10 KB · 40d7119a6cee
4 files, 37 KB in total. Each file has a sha256 in the pin's manifest; the first 12 characters are shown.
Skill folder on GitHub at be57627Related in Frontend and design
next-bundle-optimizer
Audits Next.js applications to identify and reduce browser bundle size and initial-load work by analyzing routes, dependencies and client outputs.
by VercelOfficial publisher
Claude Code, Codex, Cursor +4 · about 2.3K tokens · pinned 6 days ago
next-cache-components-optimizer
Optimize Next.js routes to display meaningful UI instantly on initial load or client navigation by encoding the optimization goal as a failing…
by VercelOfficial publisher
Claude Code, Codex, Cursor +4 · about 5.6K tokens · pinned 6 days ago
next-partial-prefetching-adoption
Enables Partial Prefetching in Next.js 16.3+ apps and guides adoption through auditing Link prefetch behavior, preserving UI in tests or manually, and…
by VercelOfficial publisher
Claude Code, Codex, Cursor +4 · about 6.7K tokens · pinned 6 days ago
remotion-interactivity
This skill teaches how to structure Remotion code so the Studio IDE recognizes and makes it interactive.
by RemotionOfficial publisher
Claude Code, Codex, Cursor +3 · about 2.4K tokens · pinned 6 days ago
remotion-interactivity
This skill teaches how to structure Remotion video components to unlock Studio interactivity features like click selection, drag-and-drop, resizing, rotation…
by RemotionOfficial publisher
Claude Code, Codex, Cursor +3 · about 4.8K tokens · pinned 6 days ago
remotion-maps
This skill enables creating map-based animations with Remotion using four different mapping libraries: Mapbox, MapLibre, MapTiler, or CesiumJS.
by RemotionOfficial publisher
Contains scripts
Claude Code, Codex, Cursor +3 · about 240 tokens · pinned 6 days ago