A critical vulnerability was discovered in React Server Components (Next.js). Our systems remain protected but we advise to update packages to newest version. Learn More

Daniel Ekeblad
Jan 2, 2024
  95
(0 votes)

Avoid Nesting Blocks – Flatten Into a Single Content Area

Blocks inside blocks is an easy way to structure data in the CMS that depends on other data. Easy for us developers that is, it only takes two or three levels of nesting before it becomes difficult for editors to work with and all the stepping in and out of blocks will quickly make them forget where in the structure they are and they feel lost.

After taking a quick look at what the CMS offers today to make this easier I will show a solution we have come up with that puts everything in one content area to give a easy overview while at the same time keeping the nested relationship clear.

What the CMS offers

Here are three features offered by Optimizely CMS that can be used to make it easier to work with nested content, but in the end is it still nested blocks.

Block properties

Oldest of the three features is the ability to use block types as properties on other content types. This type of properties render the properties of a block like they are a part of the content type the property is in. While more often used by developers to avoid repeating themselves, block properties are like an alternative to using a content reference to a block.

Quick edit

Quick edit helps to reduce the confusion of nesting blocks by making it possible to edit blocks in a content area inside a pop-up. (Added in CMS UI 11.32.0, released in January 2021)

List properties

List properties are not new but in 12.18.0 (released in March 2023) it got a big update. You can now add a list of blocks to a content type and edit them all as a part of the page. If you have not seen this feature yet it's like a content area but it's content is rendered like block properties. It's a very nice feature, but not ideal for nesting. When I experimented with this feature a couple of months ago I placed a block list property inside a block I used as a block list property and quickly found myself unsure of what outer block I was adding inner blocks to and scrolling a lot up and down in the editing interface. I also encountered and reported a bug that caused problems with saving when editing inner blocks in this kind of setup.

What we built

The built in features listed above have their use cases and limitations, for example can you not reuse block- and list-properties. We have come up with a design where we flatten a three level structure into a single content area using special purpose blocks. We use this technique on pages with a flexible design where the page is rendered with one or more sections, sections containing one or more columns and all blocks fall into one of the columns.

Take a look at the image below showing a drawing how one of our customers use this system. A section of a page with two columns, the first with a width of 2/3 and the second 1/3. The wider column has four big links to popular information pages and the smaller column have smaller has a single block with links to various sections and services on the site.

Many sites probably implements this by adding a content area to section blocks for column blocks and a content area in column blocks for other blocks. The content area on this page looks like this:

  • Section block

  • Column block (2/3)

  • Information page 1

  • Information page 2

  • Information page 3

  • Information page 4

  • Column block (1/3)

  • Quick links block

This puts everything in-front of the editors, no need to step in and out of blocks to find where a certain block is located and moving blocks between columns or sections is as easy as reordering them inside the content area.

When it's time to render the page we have a method that loads the content area items and puts blocks inside their column and columns inside their section. In our case we deliver JSON to our frontend and the agreed contract says it's a three level structure. If you render in razor views or something else you will probably find that converting from a flat structure to a three level one at some point will make the rendering easier.

However, even this solution has it's drawbacks. I have seen a page on the previously mentioned site with a content area that has a height greater than two times of my monitor. If an editor is looking for, say, the 5th block in the 1st column of the 4th section they have to slowly scan the content area to locate the start of the 4th section and then count to the 6th block. (1 column block and then another 5 blocks.)

When we began building a new site a couple of months ago I decided to look into how to make large content areas easier to overlook and found that styling content area items to be easy and effective. In the image below it's with a quick glance clear what is a section, a column or another kind of block. Looking for the beginning of the nth section? Just count the darker colored blocks.



After the section in the image you could for example add one with a single full-width column and after that a section with two half-width.

Implementation

To create the effect above you need to do four things.

  1. Define the content types, in our scenario for section and column blocks

  2. Assign an IconClass to section and column blocks using UI descriptors.

  3. Write CSS rules targeting content area items with the specific icon classes.

  4. Register the CSS file for Optimizely to load when an editor is working in the CMS, if the project does not already have one, and add the CSS rules to said file.

Defining section and column blocks

These are two very basic type of blocks and don’t need a lot of properties. Sections could have a background color option and columns need a width selection. We use content events to automatically add a columns selected width to it’s name to show editors the most important information without they having to step into (or quick edit) the block.

Adding icon classes

Adding icons to content types makes them easier to distinguish from each other and is one of the few ways to add developer-controlled data to the html of content area properties in the CMS. Icons are added using UIDescriptors:

using EpiServer.Shell;
using MySite.Blocks;

namespace MySite.Business.UIDescriptors;

[UIDescriptorRegistration]
public class SectionUIDescriptor : UIDescriptor<SectionBlock>
{
    public SectionUIDescriptor() : base("content-icon-section")
    {}
}

// Repeat for ColumnBlock

Creating CSS rules

The CSS used to create what you saw in the image above can be seen below. The screenshot was taken on a site running Episerver.CMS 12.24 and has not been tested on early 12.x or prior mayor versions.

/* <projectRoot>/wwwroot/Styles/editmode.css */

/* Give the special marker blocks (section and column) a custom background in content areas */

span:has(.content-icon-section) {
    background-color: #ccd7ff!important;
}

span:has(.content-icon-column) {
    background-color: #f0f3ff!important
}

/* Make it look like columns are a level below sections and all other blocks are a level below columns (in content areas) */

.epi-content-area-editor .dijitIcon.dijitTreeIcon.dijitLeaf.epi-objectIcon {
    margin-left: 34px;
}

.epi-content-area-editor .dijitIcon.dijitTreeIcon.dijitLeaf.content-icon-section.epi-objectIcon {
    margin-left: 0px;
}

.epi-content-area-editor .dijitIcon.dijitTreeIcon.dijitLeaf.content-icon-column.epi-objectIcon {
    margin-left: 17px;
}

/* Custom content icons (appearing to the left in the page tree, assets pane, content area items) */
/* Optimizely uses 'Font Awesome 6 Pro' for some of their icons, no need to add that font explicitly */

.content-icon-section::before {
    padding-right: 5px;
    content: "\f2fc";
    font-family: 'Font Awesome 6 Pro';
}

.content-icon-column::before {
    padding-right: 5px;
    content: "\e361";
    font-family: 'Font Awesome 6 Pro';
}

Registering a CSS file

To tell Optimizely CMS that we have a CSS file we want to be loaded by the CMS we have to register it in module.config.

<?xml version="1.0" encoding="utf-8" ?>
<!-- <projectRoot>/module.config -->
<module>
    <clientResources>
        <add name="epi-cms.widgets.base" path="/Styles/editmode.css" resourceType="Style"/>
    </clientResources>
</module>

Finishing up

As I quickly mentioned in the section “Adding Icon classes” are content areas rather restricted. They are powerful and can do a lot, but the implementation is classified as internal and difficult to extend without resorting to ugly or hacky solutions that could break at any time without notice. Thanks to the icons we got something easily added to target in CSS and because both the coloring and the icons depends on the content type it's perfect for this scenario. I was worried that the coloring or indendation would appear in the assets pane or page tree but that was not a problem. The icon is applied everywhere but the CSS earlier only sets background color and indentation for items in content areas.

Have you ever felt lost in heavily nested blocks? Have you tried searching for a solution? If you know of another interesting way to solve this problem please share it in the comments.

Someone has pointed out that compared to nested blocks you can not quickly change order of columns or sections, you have to move one block at a time. I have begun looking into it a little and if I find something interesting then maybe I will write a blog post about it.

Jan 02, 2024

Comments

Please login to comment.
Latest blogs
A day in the life of an Optimizely OMVP: Learning Optimizely Just Got Easier: Introducing the Optimizely Learning Centre

On the back of my last post about the Opti Graph Learning Centre, I am now happy to announce a revamped interactive learning platform that makes...

Graham Carr | Jan 31, 2026

Scheduled job for deleting content types and all related content

In my previous blog post which was about getting an overview of your sites content https://world.optimizely.com/blogs/Per-Nergard/Dates/2026/1/sche...

Per Nergård (MVP) | Jan 30, 2026

Working With Applications in Optimizely CMS 13

💡 Note:  The following content has been written based on Optimizely CMS 13 Preview 2 and may not accurately reflect the final release version. As...

Mark Stott | Jan 30, 2026

Experimentation at Speed Using Optimizely Opal and Web Experimentation

If you are working in experimentation, you will know that speed matters. The quicker you can go from idea to implementation, the faster you can...

Minesh Shah (Netcel) | Jan 30, 2026