Skip to main content
The online Trailblazer Community becomes read-only Oct 30, 2026, as we move new community conversations to Slack. Read the FAQ

Discover Page and Component Type Controllers, JSON, and Scripts

Learning Objectives

After completing this unit, you’ll be able to:

  • Identify three development elements you can use to create Page Designer page and component types.
  • List the JSON file naming convention.
  • Explain how to use controllers for Page Designer pages and components.
  • Describe the difference between how B2C Commerce uses JSON and JavaScript files for page and component types.

Identify the Main Development Files

The Page Designer development landscape involves just a few essential elements. Controllers, JSON, and script files carry the heaviest load. You will learn about ISML, HTML, and CSS in a later unit.

Controllers direct the overall workflow while the supporting data and instruction files handle the specific configuration and logic. Page type JSON files describe the regions of the page where you place components. Component type JSON files describe the attributes that a merchandiser configures when using the component type and optionally, the regions within the component type.

Each JSON file pairs with a script file that includes a render function returning the page or component markup as a string. Give the JSON and script files the same name, with different extensions, and place them in the same folder.

Use Controllers to Handle Requests

Controllers are server-side scripts that handle storefront requests. When you select a category menu item or enter a search term, a controller renders a page.

A storefront controller uses the dw.experience.PageMgr API to render pages. Pages render together with their components. The components do not render separately.

Here’s basic code for the page rendering process.

var content = PageMgr.renderPage(cid, null);
// emit content (for example, write to response)

Put the call to PageMgr.renderPage() in its own controller or add it to an existing controller. To take advantage of SEO URL features, add it to the Page-Show controller.

Use a storefront controller to create a URL to a Page Designer page. That URL works anywhere you link to a page: a marketing email, the global header, the footer, or the main navigation.

Define Types with JSON Meta Definition Files

Place JSON meta definition files in these directories of your custom cartridge, or a subdirectory within them.

  • Page types: {your_cartridge}/cartridge/experience/pages
  • Component types: {your_cartridge}/cartridge/experience/components

JSON file names—and any subdirectory names within those paths—accept only alphanumeric or underscore characters.

See the B2C Commerce Page Designer JSON Schemas for schema files that describe the JSON formatting for meta definition files.

Examine storePage.json

Here’s what storePage.json looks like.

{
    "name":"Storefront Page",
    "description":"A Storefront Page Type",
    "region_definitions":[
      {
       "id":"headerbanner",
       "name":"Header Banner Region",
       "max_components": 1,
       "component_type_exclusions": [
         { "type_id": "commerce_assets.categorytile" },
         { "type_id": "commerce_assets.category" },
         { "type_id": "commerce_assets.editorialRichText" },
         { "type_id": "commerce_assets.imageAndText" },
         { "type_id": "commerce_assets.mainBanner" },
         { "type_id": "commerce_assets.photoTile" },
         { "type_id": "commerce_assets.popularCategory" },
         { "type_id": "commerce_assets.productTile" },
         { "type_id": "commerce_assets.shopTheLook" },
         { "type_id": "commerce_layouts.carousel" },
         { "type_id": "commerce_layouts.mobileGrid1r1c" },
         { "type_id": "commerce_layouts.mobileGrid2r1c" },
         { "type_id": "commerce_layouts.mobileGrid2r2c" },
         { "type_id": "commerce_layouts.mobileGrid2r3c" },
         { "type_id": "commerce_layouts.mobileGrid3r1c" },
         { "type_id": "commerce_layouts.mobileGrid3r2c" },
         { "type_id": "commerce_layouts.mobileGridLookBook" },
         { "type_id": "commerce_layouts.popularCategories" },
         { "type_id": "einstein.einsteinCarousel" },
         { "type_id": "einstein.einsteinCarouselCategory" },
         { "type_id": "einstein.einsteinCarouselProduct" }
        ]
       },
       {
        "id":"main",
        "name":"Main Region",
        "component_type_exclusions": [
          { "type_id": "commerce_assets.campaignBanner" }
         ]
        },
        {
         "id":"legalnotice",
         "name":"Legal Notice Text",
         "max_components": 1,
         "component_type_inclusions": [
           { "type_id": "commerce_assets.editorialRichText" }
          ]
        }
     ]
}

It has three regions: headerbanner, main, and legalnotice.

  • The headerbanner and legalnotice regions show only one component at a time, as specified by max_components = 1.
  • The headerbanner region uses component_type_exclusions to block several component types.
  • The main region has no max_components value, so it shows multiple components.
  • The legalnotice region uses component_type_inclusions to restrict the region to commerce_assets.editorialRichText components only.

The max_components value restricts the number of components rendered for a region at a time, but the region holds any number of components. Use this value to show different components for different customers or on different schedules—a banner region, for example, shows a winter promotion component in January and a summer promotion in July.

Examine mainBanner.json

The mainBanner.json file defines three attribute groups that the merchandiser configures: a banner image, text overlay, and shop-now link.

{
  "name": "Main Banner",
  "description": "Image, text overlay that links user to a category using the markup editor",
  "group": "commerce_assets",
  "attribute_definition_groups": [
    {
     "id": "bannerImage",
     "name": "Banner Image",
     "description": "The visual display of the banner image in the background.",
     "attribute_definitions": [
      {
       "id": "image",
       "name": "Image",
       "type": "image",
       "required": true
      }
     ]
    },
    {
     "id": "textOverlay",
     "name": "Text Overlay",
     "description": "The overlay text on top of the banner image.",
     "attribute_definitions": [
      {
       "id": "heading",
       "name": "Heading",
       "type": "markup",
       "required": true
      }
     ]
    },
    {
     "id": "shopNowLink",
     "name": "Shop Now Link",
     "description": "The Shop Now link on the banner image",
     "attribute_definitions": [
      {
       "id": "categoryLink",
       "name": "Category Link",
       "type": "category",
       "required": true
      }
     ]
    }
  ],
  "region_definitions": []
}

Write Script Files

Each page or component type requires a script file. Give the script file the same name as its matching meta definition file, with a .js extension. For a component type named headlinebanner, place both files in the same directory.

  • mycartridge/cartridge/experience/components/assets/headlinebanner.json
  • mycartridge/cartridge/experience/components/assets/headlinebanner.js

The script file includes a render function that returns the markup for the page or component type as a string. Usually, render calls an ISML template, passing information about the page or component type and its content. Use the dw.util.Template API to render the markup from the template. Don’t use dw.template.ISML—it doesn’t return a string and writes the markup directly to the response stream.

The context object passed to render differs between page and component script files.

For page script files, the context is dw.experience.PageScriptContext, which gives access to:

  • Context.page: The currently rendered page
  • context.renderParameters: Parameters passed to PageMgr.renderPage(pageID, parameters)
  • Context.content: Attributes set in the custom logic

For component script files, the context is dw.experience.ComponentScriptContext, which gives access to:

  • Context.component: The currently rendered component
  • context.componentRenderSettings: Render settings provided by the hosting region
  • Context.content: Attributes as defined in the meta definition file and configured by the merchandiser in the visual editor, or as defined in the custom logic

Apply Decorators to Page Designer Pages

You can implement decorator strategies in two ways. First, write the page type script to accept a custom decorator as a parameter, with a fallback to a default decorator. Alternatively, use a controller to select a decorator dynamically—for example, serving an AJAX decorator for AJAX requests and a full-page decorator for all others.

Examine storePage.js

Here’s what storePage.js looks like.

'use strict';
/* global response */
var Template = require('dw/util/Template');
var HashMap = require('dw/util/HashMap');
var PageRenderHelper = require('*/cartridge/experience/utilities/PageRenderHelper.js');
/**
* Render logic for the storepage.
*
* @param {dw.experience.PageScriptContext} context The page script context object.
* @param {dw.util.Map} [modelIn] Additional model values created by another cartridge. This will not be passed in by Commerce Cloud Platform.
*
* @returns {string} The markup to be displayed
*/
module.exports.render = function (context, modelIn) {
    var model = modelIn || new HashMap();
    var page = context.page;
    model.page = page;
    model.content = context.content;
    // automatically register configured regions
    model.regions = PageRenderHelper.getRegionModelRegistry(page);
    if (PageRenderHelper.isInEditMode()) {
        var HookManager = require('dw/system/HookMgr');
        HookManager.callHook('app.experience.editmode', 'editmode');
        model.resetEditPDMode = true;
    }
    model.CurrentPageMetaData = PageRenderHelper.getPageMetaData(page);
    // no pagecache setting here, this is dynamically determined by the components used within the page
    // render the page
    return new Template('experience/pages/storePage').render(model).text;
};

The storePage.js renders the type defined in storePage.json by calling new Template(...).render. The primary page content lives in the storePage.isml template and storePage.css.

Examine mainBanner.js

Here’s mainBanner.js:

'use strict';
/* global response */
var Template = require('dw/util/Template');
var HashMap = require('dw/util/HashMap');
var URLUtils = require('dw/web/URLUtils');
var ImageTransformation = require('*/cartridge/experience/utilities/ImageTransformation.js');
/**
* Render logic for the storefront.MainBanner component
* @param {dw.experience.ComponentScriptContext} context The Component script context object.
* @param {dw.util.Map} [modelIn] Additional model values created by another cartridge. This will not be passed in by Commerce Cloud Platform.
*
* @returns {string} The markup to be displayed
*/
module.exports.render = function (context, modelIn) {
    var model = modelIn || new HashMap();
    var content = context.content;
    model.heading = content.heading;
    model.image = ImageTransformation.getScaledImage(content.image);
    model.categoryLink = URLUtils.url('Search-Show', 'cgid', content.categoryLink.getID()).toString();
    // instruct 24 hours relative pagecache
    var expires = new Date();
    expires.setDate(expires.getDate() + 1); // this handles overflow automatically
    response.setExpires(expires);
    return new Template('experience/components/commerce_assets/mainBanner').render(model).text;
};

Up Next

In this unit, you explored the code underlying Page Designer page and component types—controllers, JSON, and scripts. You learned how JSON meta definition files describe page regions and component attributes, and how script files render markup by using the dw.util.Template API. The next unit covers ISML, HTML, and CSS.

Resources

Share your Trailhead feedback over on Salesforce Help.

We'd love to hear about your experience with Trailhead - you can now access the new feedback form anytime from the Salesforce Help site.

Learn More Continue to Share Feedback