SXA Column Splitter for Sitecore JSS

In previous blog I described how to setup SXA Grid System with Sitecore JSS. In this post we’ll learn how to implement SXA Column Splitter component in JSS.

In standard SXA this component allows to easily split content into manageable columns. With some changes to Layout Service we can achieve similar behavior in Sitecore JSS:

Source code described in this blog is available on Github.

Items configuration

Before we start we need to configure grid system for our SXA site following SXA Grid System with Sitecore JSS. We only need to configure site settings item, select grid mapping and optionally for bootstrap to update Grid Definition item. Changes to components, or pipelines described in that post are not necessary.

Next we need new template for Rendering Parameters inheriting from /sitecore/templates/Feature/Experience Accelerator/Page Structure/Rendering Parameters/ColumnSplitter. This will give the ability to configure size of each column in the splitter:

Optionally we can add _Standard Values item for this template, with default column splitter values (e.g. two column split on desktop and one column on mobile). Hint: you can edit Columns field using Raw values in format: {item guid}|{item guid}.

Next we need to create new Placeholder Settings item for placeholder column and assign renderings which you want to insert to the columns to Allowed Controls field:

We can now add new Json Rendering item for our Column Splitter component pointing to JSS implementation:

In this item we would need to:

  • Select template created in first step in Parameter Template field:
  • Select Add Column and Remove Column in Experience Editor Buttons field:
  • Add column placeholder to Layout Service Placeholders field:

Now we need to allow to add our new component into the layout, we do it by adding Column Splitter component to Allowed Controls of your main placeholder settings items.

We can add now column splitter to the layout and check how Layout Service returns it. If you setup two columns in rendering parameters Standard Values item for Desktop and one column on Mobile you should have something similar to this:

We need to solve two issues here:

  1. Change field ColumnWith{column} in params from items guids to grid system CSS classes, which can be used easily in JSS.
  2. Inform JSS component which columns are actually selected by Content Author (we can use EnabledPlaceholders field from params) and return components added to the column placeholder in proper way to allow assigning different content to each column.

Pipelines Code

To solve first issue we need to add our custom processor to renderJsonRendering pipeline, which will read grid definition items and replace IDs with class names in output JSON:

We need to call this processor after pipeline is initialized, with following config patch:

If we call layout service again, ColumnWith{column} params will be replaced by proper CSS classes.

To solve second issue, we need to first understand the problem which we are challenging. You probably know that JSS and Layout service uses Sitecore Dynamic Placeholders feature, but it doesn’t support it fully. You may notice that every component  added into single rendering’s nested placeholder have the same dynamic placeholder key, eg.: /jss-main/column-{guid}-0. It’s because Layout Service doesn’t support dynamic placeholders within one rendering (in other words the suffix within rendering is not unique and it’s always -0).

This would be problematic for our Column Splitter, which in SXA is implemented as a single rendering which generates multiple placeholders inside. Because placeholders are not unique, if we would add a component into one column, it would be rendered multiple times in every column. We can address this issue by replacing one resolver and one pipeline processor in Layout Service:

Replace default placeholderResolver configured in Layout Service for JSS with our custom implementation, which returns placeholders count, based on EnabledPlaceholders field from rendering parameters:

Replace RenderPlaceholder processor from renderJsonRendering pipeline with custom implementation, which split placeholders in output JSON into different nodes:

The final config patch looks like this:

If we would call layout service again we shall have following results:

Hint: there is alternative way of solving the issue of returning dynamic placeholders within a single rendering, which will not split placeholders into different nodes in Json, but returns all in single node one by one. Instead of changing RenderPlaceholder processor you would need to replace Sitecore.JavaScriptServices.ViewEngine.LayoutService.Serialization.PlaceholderTransformer (this is the place where Sitecore transforms placeholders into Json objects). This way may be more close to what Sitecore is doing by default, when returning placeholders in Layout Service, but I found it more problematic to handle in JSS, especially in Experience Editor, where Layout Service returns more elements for a placeholder. It also requires to replace some services in Layout Service dependency injection.

JSS implementation

In our component implementation in JSS we need to use EnabledPlaceholders parameter to render as much placeholders as needed and dynamically change each placeholder name, so it will take rendering from proper Json node. React implementation would look like this:

Finally let’s add some components to our columns, we can do it from Experience Editor. We can also change amount of columns (with plus and minus buttons) and configure it’s layout from Column Splitter Component Properties dialog. When we look at our Presentation Details after adding components, we shall have them with unique placeholder keys:

Layout Service shall return each as separate node in Json:

Hint: there is an issue in SXA 9.3 and SXA 10 related with Standard Values in rendering parameters, which are not applied in Experience Editor after adding a component to the page. Editor needs to open and close component properties dialog to apply it. For details check CS0194308 with Sitecore Support. On Sitecore 9.3 you may also encounter issue when adding components to the nested placeholders. Details and solution is described here: This issue is already addressed in Sitecore 10.