Page Designer Skill This skill guides you through creating custom Page Designer page types and component types for Salesforce B2C Commerce. Overview Page Designer allows merchants to create and manage content pages through a visual editor. Developers create: Page Types - Define page structures with regions 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. Page Types Meta Definition (pages/homepage.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) 'use strict' ; var Template = require ( 'dw/util/Template' ) ; var HashMap = require ( 'dw/util/HashMap' ) ; module . exports . render = function ( context ) { var model = new HashMap ( ) ; var page = context . page ; model . put ( 'page' , page ) ; return new Template ( 'experience/pages/homepage' ) . render ( model ) . text ; } ; Page Template (templates/experience/pages/homepage.isml) < isdecorate template = " common/layout/page "
< isscript
var PageRenderHelper = require('*/cartridge/experience/utilities/PageRenderHelper'); </ isscript
< div class = " homepage "
< div class = " hero-region "
< isprint value = " ${PageRenderHelper.renderRegion(pdict.page.getRegion('hero'))} " encoding = " off " /> </ div
< div class = " content-region "
< isprint value = " ${PageRenderHelper.renderRegion(pdict.page.getRegion('content'))} " encoding = " off " /> </ div
< div class = " footer-region "
< isprint value = " ${PageRenderHelper.renderRegion(pdict.page.getRegion('footer'))} " encoding = " off " /> </ div
</ div
</ isdecorate
Component Types Meta Definition (components/banner.json) { "name" : "Banner" , "description" : "Promotional banner with image and CTA" , "group" : "content" , "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 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 ; } ; 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 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 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 Object 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" } ] } ] } 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 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 Best Practices Use dw.util.Template for rendering (NOT dw.template.ISML ) Keep components self-contained - avoid cross-component dependencies Provide default values for optional attributes Group related attributes in attribute_definition_groups Use meaningful IDs - they're used programmatically Don't change type IDs after merchants create content Detailed Reference For comprehensive attribute documentation: Meta Definitions Reference - Full JSON schema Attribute Types Reference - All attribute types with examples