- 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 |
Auf GitHub ansehen