A common requirement on most Episerver CMS projects is the ability for content editors to create a number of pages, or, blocks that contain one or more images or links. In Episerver when we do this we use a ContentReference property to allow a content editor to create a reference to an asset. Usually, within a controller, or potentially a view, we use one of the Episerver APIs to translate the Id of the content reference into a friendly Url that links to that asset.

As good developers, one of the architectural goals we want to follow is DRY (don't repeat yourself), e.g. avoid writing unneeded/duplicate code. In today's post, I'm going to explain how we can structure Episerver to avoid breaking the DRY principle when it comes to defining and rendering these common components.

Common Components

The most common examples of these common sets of properties are:

  • An image (URL and Alt text)
  • Responsive Image (desktop URL, mobile URL, alt text)
  • Buttons/CTA (button text, button URL)
  • Link (URL, title, link text)

Depending on your design, you will likely bump into other similar examples. When you start building a new project, my advice is to think of a nice and clean way to deal with these tasks in code. One challenge when dealing with these common types is giving consistency throughout the editor experience. You do not want the anatomy of how editors are asked to add images to change on each page. You would not want the image picking experience to vary, for example you would not want an image picker to only contain a name property on one page, a name and an alt text property on another page and on a third page the labels are all different. To create an excellent editor experience, you need to make sure that the way in which editors interact with these common types is consistent. Keep reading to learn how I do this!

Interface Or Block

The two most common ways for dealing with this problem is to use either an interface or, to create a local block to represent each common component.

The Interface Approach: For each common component you define an interface that represents the components data structure. You then implement that interface on a page or block wherever you want the content editor to be able to use that type:

In your controller/view model, you can then create a common helper method that converts the interface to a view model. This saves on a lot of code and makes unit testing easier:

This approach is OK. It is definitely a lot better than manually defining the same properties over and over again on each page and block. It reduces the number of unit tests that need to be written. A factory can be used to generate any view models based on the interface. The downside is that you can not reuse the same CMS meta-data between all the different blocks and pages. This can lead to an inconsistent editor experience. Also, what happens if you need to define more of one type on a page or block? For these reasons, I recommend you use the local read-only block approach, which I'll cover next.

Local Readonly Blocks: The local read-only block option is pretty similar to the interface approach. Instead of using an interface, we create a local block. In the local block, we set the appropriate meta-data which can be used on all pages/blocks where the block is implemented. An example of a local image block can be seen below:

You can then apply the block onto your pages/blocks, like so:

When you think about tackling this challenge of dealing with common components, local blocks is the approach I'd recommend. To make the block hidden from the select new block screen within the CMS, note the AvailableInEditMode = false is applied on ImageBlock.

When working with Episerver, every website will have a requirement to render images and links on a page. Without a good technical approach, you'll end up writing a lot of duplicate code. Using a local block approach will mean a consistent editor experience within the CMS. This will also minimize the amount of boilerplate property code you need to write. The side-effect of this change is fewer unit tests that need to be written. Using local blocks will make your life a lot easier so start using them today! Happy Coding 🤘