Customize SFRA

Storefront Reference Architecture (SFRA) is extensible. You can customize SFRA without editing the app_storefront_base cartridge or other plug-ins, such as plugin_applepay. The extensible design lets you more easily take security updates, bug fixes, and adopt new features.

Only existing customers can access some of the links on this page. Visit Salesforce Commerce Cloud GitHub Repositories and Access for information about how to get access to the Commerce Cloud repositories.

Implementing a site requires at least one custom cartridge. However, if you intend to create multiple sites, we suggest you create multiple custom cartridges. Each cartridge can separate functionality specific to a brand or locale, so that you can reuse most of your cartridge stack for a new site.

All available LINK and plug-in cartridges are on GitHub. Plug-in cartridges are in the SFRA project in GitHub. You can Include link and plug-in cartridges on your cartridge path and base path as you would any other cartridge in your stack.

Don't rename the app_storefront_base cartridge or other provided plug-ins. Keep your version of the app_storefront_base cartridge and other plug-ins up-to-date. By doing so, you ensure that you have the latest bug fixes and performance improvements.

If you globally installed sgmf-scripts through npm, follow these steps to add a custom cartridge:

  1. Create a folder for your project. For example:

  2. Navigate into the folder.

  3. Install the sgmf-scripts node.

  4. Use the sgmf-scripts --createCartridge command.

    These directories and files are created in your folder:

  5. Install the dependencies required by the new cartridge:

To see all the available SFRA-commands, enter sgmf-scripts --help.

If you want to avoid globally installing sgmf-scripts, follow these steps to add a custom cartridge:

  1. Create a folder for your project. For example: mysite.

  2. Navigate into the folder.

  3. Install the sgmf-scripts node via npm.

  4. Use Node.js to call the sgmf-scripts createCartridges command.

    The following directories and files are created in your folder:

  5. Install the dependencies required by the new cartridge:

To see all the available SFRA-commands, enter sgmf-scripts --help.

Because you're not modifying the app_storefront_base cartridge directly, Salesforce B2C Commerce provides a mechanism to selectively override CSS styles and client-side JavaScript. In the package.json file, the paths property lists every cartridge with CSS and client-side JavaScript functionality customized in your site. When building your cartridge stack, the paths property lets you import functionality from CSS and JavaScript files in other cartridges and selectively override it. When you use the command-line compile tool, it compiles the JavaScript and CSS across cartridges.

  1. Navigate to the top level of your custom cartridge. For example, mysite.

  2. Open the package.json file and modify the paths property. The paths.base property points to the local directory containing app_storefront_base. Add properties for the cartridges you want to import functionality from.

    Example: adding plugin_ratings, plugin_reviews, and app_storefront_base to a custom cartridge:

  3. Import the CSS files you want to include and override into the my_repository/cartridges/my_cartridge/cartridge/client/default/myfile.scss file.

    The following sample global .scss file inherits most of its styles from the base cartridge. The location of the base cartridge is defined in package.json. This example uses the base property defined in the package.json to import style sheets.

  4. Use the path object to include JavaScript modules from other cartridges when you want to inherit and override client-side JavaScript functionality.

    The following example uses the path described by the base property to require in a client-side script in the base cartridge.

After you create your custom cartridge, refer to Create and Manage Sites for Developmen

  • Create unit tests for models.
  • Check for possible naming collisions between cartridges.

Anti-patterns:

  • Don't override pdict variables.
  • Don't delete properties off the pdict.
  • Don't replace something in pdict with something else with a different signature.

Each cartridge is built independently, uploaded, and then added to the cartridge stack. You can change and upload cartridges independently, as long as you develop against same code version.

The command-line tools are designed to work in the top-level folder of each plug-in or cartridge repository. For example, watch only works for the directory in which you start it.

Cartridges provided by B2C Commerce have to be built only once for each version and uploaded. You can create a separate build script for routine maintenance of cartridges you update and upload. In contrast, you could also create a lightweight script that lets you dynamically build and upload just the cartridges you're currently working on.

The system searches the cartridge path for the first template that matches the specified name and location. To override a template, create a custom cartridge. In the custom cartridge, include a template with the same name as the template you want to override.

The location of your template in your cartridge must match the location of the original template in the original cartridge. Finally, add your custom cartridge to the cartridge path, placing your cartridge to the left of the original cartridge.

Decorator Templates in SFRA

There are only two decorator templates:

  • page.isml: Contains navigation information
  • checkout.isml: Doesn't contain navigation information. Removing navigation information has been shown to improve the percentage of cart abandonment.

These templates are located in app_storefront_base/cartridge/templates/default/common/layout/.

All of the pt_..._VARS and pt_..._UI templates that existed in previous versions of SiteGenesis were removed.

The navigation for a page is set through the new decorator templates. Client-side scripts and CSS files are set for each template using the assets.addJs and assets.addCss functions for each template. The top of every page template contains code similar to the following:

You can put _.css files and _.js files with the same names and in the same locations as indicated in the original template. This strategy overrides any like-named _.css files and _.js files in any cartridge to the right of the current cartridge on the cartridge path.

You can override CSS and client-side JavaScript for a specific template. For example, if the template declares the following assets:

You can create a productDetail.js and detail.css and place them in the same location as indicated in the template. The platform uses the first template with the correct name and location that it finds on the cartridge path.

When creating the CSS file you want to customize, create a file with the same name and location as the file you 're customizing. In your file, import the original file.

Example: Customizing Detail.Scss.

Usually, business logic isn't included in app_storefront_base templates. Any logic in the templates is specific to rendering. The app_storefront_base cartridge demonstrates recommended strategies for using ISML:

  • iscache: Set by default to 24 hours. We recommend changing this value in the controller.
  • isset: Only used to set a variable used by isinclude. Don't use complex expressions in the isset.
  • isscript: Only used to add client-side JavaScript/CSS to the page using the asset module.
  • ismodule: Only used for content assets.
  • iscontent: Change this value in the controller.
  • iscookie: Change this value in the controller.
  • isredirect: Change this value in the controller.
  • isstatus: Change this value in the controller.
  • isobject: Used to wrap products for analytics and the Storefront Toolkit.

Models in SFRA provide a JSON object layer for the application. The models convert objects returned by the B2C Commerce script APIs into pure JSON objects designed for the storefront. The models also apply business logic for the storefront.

A model passed in for rendering a template is assigned to the viewData variable. You can add extra properties to the viewData variable — properties that are not provided by the app_storefront_base cartridge.

Controllers create and update models. To customize a model, you create the model and then add data to it that can be used for rendering.

Anything in the modules folder or the toplevel folder is globally available and extendable.

You can customize models to:

  • Extend the model to include more data to be used when rendering the template
  • Add extra models with objects used to render templates
  • Change models to be used to render templates in a different framework

You can customize SFRA controllers and routes to work for your own storefront.

It's important to understand when to extend a controller and when to override it, because this decision can significantly impact functionality and performance.

  • When do I want to Override?

    It's best to override if you want to avoid executing the middleware of the controller or script you're modifying.

    When extending a controller, you first execute the original middleware, and then execute the additional steps included in your extension. If the original middleware steps include interaction with a third-party system, that interaction is still executed. If your extension also includes the interaction, the interaction is executed twice. Similarly, if the original middleware includes one or more steps that are computationally expensive, you can avoid executing the original middleware.

  • When do I want to Extend?

    If the middleware you want to override is looking up a string or performing inexpensive operations, you can extend the controller or module.

  • How do I extend or Override?

    Use the module.superModule mechanism to import the functionality from a controller and then override or add to it.

This example customizes the product detail page to include product review information. The code for this example is available in the Plugin_reviews demo cartridge.

In this example, the Product.js controller uses the following APIs for customization:

  • module.superModule: Imports functionality from the first controller with the same name and location found to the right of the current cartridge on the cartridge path.
  • server.extend: Inherits the existing server object and extends it with a list of new routes from the super module. In this case, it adds the routes from the module.superModule Project.js file.
  • server.append: Modifies the Show route by appending middleware that adds properties to the viewData object for rendering. Using server.append causes a route to execute both the original middleware chain and any additional steps. If you're interacting with a web service or third-party system, don’t use server.append.
  • res.getViewData: Gets the current viewData object from the response object.
  • res.setViewData: Updates the viewData object used for rendering the template. Create your customized template in the same location and with the same name as the template rendered by the superModule controller. For example, if this controller inherits functionality from app_storefront_base, the rendering template depends on the product type being rendered. The rendering template can be either product/productDetails, product/bundleDetails, or product/setDetails.

If you want to completely replace a route, rather than append it, use module.superModule to inherit the functionality of the controller and route you want to replace. Then register the functions you want the route to use.

Example: replacing the Product-Varation route

In your custom cartridge, create a Product.js file in the same location as the Product.js file in the base cartridge. Use the following code to import the functionality of Product.js and redefine it.

We recommend that you replace a route to change individual steps in a middleware chain.

You can't change a step in the middleware chain. However, you can either replace the whole route or append a new step after the chain completes, but before the template renders. Usually, if you're appending a step, it's to override or add data to the ViewData object.

Be careful when appending existing routes. If you append a step, the existing middleware chain executes no matter what the appended step does. Therefore, it’s possible to execute the route twice. If the route calls a web service or updates a third-party system, such as an inventory management system, it can take the same action twice.

Information about middleware filtering classes is available in the SFRA JSDoc in the server-side documentation for Global.

You can use the middleware functions provided by Commerce Cloud or create your own. We recommend that you replace a route when changing access.

These middleware filtering functions are provided by Commerce Cloud:

  • get: Filter for get requests
  • htt: Filter for http requests
  • https:Filter for https requests
  • include: Filter for remote includes
  • post: Filter for post requests

If the request doesn't match the filtering condition, the function returns an Error with the text Params do not match route.

In general, customizing a controller with a web service or third-party call uses the same mechanisms as other types of customizations. However, it's important to make sure that you don't execute the controller twice. Executing the controller twice is possible if you use server.append to customize the ViewData object passed to the rendering template. Instead, simply replace the route entirely.