Develop Email Builder components
Developer preview feature
The Email Builder feature is currently not fully functional, and primarily intended to allow technical users to familiarize themselves with the development process for email components. Expect the feature to be updated and extended in upcoming releases.
The related API is marked as experimental and usage will result in warnings when compiling your project. The warnings are treated as errors for reporting purposes. To use the code, you need to suppress the warnings.
What should you do with this feature?
- DO try out development of Email Builder components.
- DO feel free to share your feedback with the Kentico Product team.
- The feature currently CANNOT be used to create and send emails. You can test components in the Email Builder UI, but cannot save the content.
The Email Builder framework consists of components, which are implemented using ASP.NET Blazor as Razor components.
Email Builder components fit together in the following hierarchy to form an email:
- Email templates – the top-level component that sets the email’s overall design, as well as any fixed content, such as a header and footer. Templates contain one or more editable areas.
- Editable areas – parts of templates that are empty by default, but can be edited using the Email Builder visual interface.
- Sections – blocks of markup that specify a layout for email content, typically containing one or more columns. Can store an arbitrary number of widget zones – areas where content creators place widgets. Sections are fully customizable, and as a developer, you have absolute freedom in the way you set each section’s layout.
- Widget zones – allow content editors to insert widgets. Each zone can contain any number of widgets (depending on the design limitations of the parent template and section).
- Widgets – reusable components that represent individual pieces of email content, such as text, images, CTA links, etc. Widgets can be easily manipulated by content editors and other non-technical users.
MJML markup
The markup of Email Builder components can either be standard HTML or use the MJML markup language – a framework for easily creating responsive emails.
Combining HTML and MJML Email Builder components is not supported
When planning to use Email Builder for a project, you need to decide whether to use HTML or MJML components. You cannot combine both types within a single project. Once you enable MJML support, HTML components will no longer work.
To enable MJML support for Email Builder components:
- Install the Kentico.Xperience.Mjml NuGet package into your project.
- Call
AddMjmlForEmails
in your application’s startup code (Program.cs). - Replace the default section (which contains HTML markup) with an MJML section:
- Assign the identifier of a suitable MJML section into
EmailBuilderOptions.DefaultSectionIdentifier
. - Set
EmailBuilderOptions.RegisterDefaultSection
tofalse
.
- Assign the identifier of a suitable MJML section into
using Kentico.Web.Mvc;
using Kentico.EmailBuilder.Web.Mvc;
using Kentico.Xperience.Mjml;
// ...
builder.Services.AddKentico(features =>
{
// Enables Email Builder
features.UseEmailBuilder();
// ...
});
builder.Services.Configure<EmailBuilderOptions>(options =>
{
// Allows Email Builder for the 'Acme.Email' content type
options.AllowedEmailContentTypeNames = ["Acme.Email"];
// Replaces the default Email Builder section
options.DefaultSectionIdentifier = "Acme.DefaultMjmlSection";
options.RegisterDefaultSection = false;
});
// Enables MJML markup processing for Email Builder components
builder.Services.AddMjmlForEmails();
// ...
MJML limitations
The <mj-attributes> and <mj-style inline=“inline”> MJML tags are not fully supported within Email Builder components, and we strongly recommend that you avoid using them.
Blazor development best practices
All Email Builder components must be developed as ASP.NET Razor components (Blazor). If you are new to Blazor development, start by familiarizing yourself with at least the following .NET documentation:
- ASP.NET Core Blazor – a general overview of Blazor development
- ASP.NET Core Razor components – the basics of Razor component development
- ASP.NET Core Razor component lifecycle – explains the component lifecycle and how to use lifecycle events
Additionally, follow these practices when developing Email Builder components:
- Use Dependency injection to access instances of Xperience API services in the markup or code of your components.
- Consider splitting components into two files – HTML/MJML and Razor markup in a Razor file (
.razor
) and C# code in a code-behind file defined as a partial class (.razor.cs
).- For simple components that mostly consist of markup, you can keep everything within the
.razor
file. But components with any significant code logic are easier to develop and maintain with a separate code-behind file. - See Partial class support to learn more about splitting Razor components into multiple files.
- Note: The component code-behind must belong in the same namespace as the Razor file code, which uses an automatic namespace in format
<assembly name>.<folder structure>
by default. If you use a complicated folder structure to store components, you can override the Razor file’s namespace explicitly using the @namespace directive.
- For simple components that mostly consist of markup, you can keep everything within the
- If you wish to share reusable content between Email Builder components, prepare separate Razor components and nest them within the Razor markup.
- For MJML components, you can create MJML files with reusable content and add them using <mj-include>.
- If you plan to create a large number of Email Builder components, consider placing them into a dedicated Razor class library (RCL).
- Install the Kentico.Xperience.WebApp NuGet package into the RCL project.
- The RCL must have class discovery enabled – add the
[assembly: CMS.AssemblyDiscoverable]
attribute to a class within the project, for example Properties/AssemblyInfo.cs. - Add a project reference from your main Xperience web project to the RCL.
Global import of using statements
To set up your Razor component folder for Email Builder development, you can create an _Imports.razor file in the root, and add a @using Kentico.VisualBuilderComponents.Rcl.Components
statement. The namespace allows you access Email Builder components, such as editable areas and widget zones, within your Email Builder Razor components. You can also add using statements for any other frequently used namespaces.
Alternatively, you can add using statements for the Kentico.VisualBuilderComponents.Rcl.Components
namespace directly into the code of individual components.
Styles, images and assets for Email Builder components
Your emails may often require custom CSS, images and other assets to achieve their desired appearance. When composing emails from Email Builder components, styling will need to be part of the component definitions.
Use one of the following options to include CSS styles:
- The most recommended option for styling emails is to use inline CSS code for your HTML or MJML elements.
- If you prefer to embed CSS, you can place style definitions into the <head> tag of your templates (for MJML templates, use the <mj-head> → <mj-style> MJML components).
- For MJML components, you can include CSS from external files using <mj-include> components.
To include font definitions, prepare and include @font-face
declarations or import web fonts. For MJML components, you can include .css files with font declarations using <mj-font> component’s the <head> tag of your templates.
Carefully consider how to store images and other files assets for Email Builder components:
- For local development and testing, you can store images or other assets in the Xperience project’s wwwroot/Content/Images folder.
- For production environments that send emails to real recipients, images and other file assets must be linked from publicly available storage, such as a CDN.
Images in MJML components
For components with MJML markup, include images using the appropriate MJML components, such as <mj-image> or <mj-hero>.
Templates
Templates are the top-level component that sets the email’s overall design, as well as any fixed content, such as a header and footer. The template content is defined through HTML or MJML markup. Templates also define areas that can be edited using the Email Builder visual interface.
Develop templates as ASP.NET Razor components and follow our Blazor development best practices.
Email Builder scripts and styles
Templates must load scripts and styles required for the Email Builder UI. Add the following components into the template markup:
<EmailBuilderStyles />
– renders links for required stylesheet files. Call within the <head> tag of the email markup (within the the <mj-head> component for MJML templates).<EmailBuilderScripts />
– renders required script tags and script file links. Call before the closing </body> tag of the page code (at the end of the <mj-body> component for MJML templates).
These components ensure that the scripts and styles are loaded when the email is being edited in the Email Builder visual interface.
@using Kentico.VisualBuilderComponents.Rcl.Components
<html>
<head>
...
<EmailBuilderStyles />
</head>
<body>
...
<EmailBuilderScripts />
</body>
</html>
@using Kentico.VisualBuilderComponents.Rcl.Components
<mjml>
<mj-head>
...
<EmailBuilderStyles />
</mj-head>
<mj-body>
...
<EmailBuilderScripts />
</mj-body>
</mjml>
Editable areas
Editable areas mark the locations in your emails where you want users to edit content by adding sections and widgets. To place editable areas, add EditableArea
components within the template’s body:
<EditableArea AreaIdentifier="MainContent" DefaultSectionIdentifier="Acme.ExampleEmailSection" />
The AreaIdentifier
property of each editable area must be a string without whitespace characters that is unique within the context of the given template.
The DefaultSectionIdentifier
property allows you to set the section which is automatically added into the area for new emails and in cases where an editor removes the last section from the area. If not specified, the area uses the global default section set for the Email Builder feature.
Register templates
Templates must be registered using the RegisterEmailTemplate
assembly attribute. You can add the attribute to the start of the template’s Razor component code-behind class (if you have created one), or use a central class to register all of your Email Builder components, e.g., ComponentRegister.cs
.
Specify the following parameters for the attribute:
identifier
– a unique identifier for the template. We recommend using a unique prefix in your identifiers to prevent conflicts with other templates, for example matching your company’s name.name
– the name used to identify the template when creating emails in the administration interface.componentType
– theSystem.Type
of the template’s Razor component class.
You also need to specify the email content types for which the template will be available via the attribute’s ContentTypeNames
property. Set the value to an array of email content type code names.
using Kentico.EmailBuilder.Web.Mvc;
[assembly: RegisterEmailTemplate(
identifier: "Acme.ExampleEmailTemplate",
name: "Example template",
componentType: typeof(ExampleEmailTemplate),
ContentTypeNames = ["Acme.Email"])]
Once your templates are registered, they appear in the template selector when creating new emails of the specified content types in an email channel.
Template notes and limitations
- For templates with MJML markup, you can use MJML’s <mj-section> components, but only for fixed hard-coded content. Do not place editable areas inside
<mj-section>
tags. For content that is managed in editable areas, add the<mj-section>
tags into Email Builder sections. - Email Builder templates currently do not support configurable properties.
- It is currently not possible to change the Email Builder template of an existing email. You can only assign the template when creating new emails.
Sections
Sections are blocks of markup that specify a layout for email content, typically containing one or more columns. Section content is defined through HTML or MJML markup. Sections contain one or more zones where editors can add widgets using the Email Builder visual interface.
Develop sections as ASP.NET Razor components and follow our Blazor development best practices.
Sections with MJML markup
When using MJML markup for Email Builder components, define the section’s layout using <mj-section> and <mj-column> components.
The options for column width are restricted to the following predefined values:
- Percent values:
- 1%, 2%, 3%, 5%, 10%, 15%, … , 95%, 100% (multiples of 5% between 5 and 100)
- 33.33333333333333% and 66.66666666666666%
- The sum of all column widths must be 100%.
- Length values in px: 1, 2, 3, 5, 10, 20, …, 590, 600px (multiples of 10px between 10 and 600)
Note: All section content must be placed within an <mj-section> component. Anything outside may not be rendered correctly.
Widget zones
Widget zones allow content editors to insert widgets. Each zone can contain any number of widgets, depending on the design limitations of the parent template and section. Every section must contain at least one widget zone – sections without widget zones are not supported.
To place widget zones into section markup, add WidgetZone
components. The ZoneName
property of each zone component must be a string without whitespace characters that is unique within the given section.
@using Kentico.VisualBuilderComponents.Rcl.Components
<mj-section>
<mj-column width="33.33333333333333%">
<WidgetZone ZoneName="ZoneA" />
</mj-column>
<mj-column width="33.33333333333333%">
<WidgetZone ZoneName="ZoneB" />
</mj-column>
<mj-column width="33.33333333333333%">
<mj-spacer height="55px" width="50%"></mj-spacer>
<mj-image src="..." href="..."></mj-image>
<mj-spacer height="155px"></mj-spacer>
</mj-column>
</mj-section>
@code {
// ...
}
Register sections
Sections must be registered using the RegisterEmailSection
assembly attribute. You can add the attribute to the start of the section’s Razor component code-behind class (if you have created one), or use a central class to register all of your Email Builder components, e.g., ComponentRegister.cs
.
Specify the following parameters for the attribute:
identifier
– a unique identifier for the section. We recommend using a unique prefix in your identifiers to prevent conflicts with other sections, for example matching your company’s name.name
– the name displayed in the section selection dialog within the Email Builder UI.componentType
– theSystem.Type
of the section’s Razor component class.
You can also set the following optional properties:
Description
– additional description text displayed as a tooltip in the Email Builder section selection dialog.IconClass
– the font icon class displayed in the section selection dialog.
using Kentico.EmailBuilder.Web.Mvc;
[assembly: RegisterEmailSection(
identifier: "Acme.ExampleEmailSection",
name: "Example section",
componentType: typeof(ExampleEmailSection),
IconClass = "icon-rectangle-a")]
Once your sections are registered, they appear in the selection dialog displayed when adding sections in editable areas in the Email Builder UI.
Assign a default section
Editable areas in Email Builder have a default section, which is automatically added when the area is empty. This includes new areas where content editors have not yet placed any other section, and scenarios where an editor removes the last section from an area.
The system provides a built-in Default section for this purpose. This Default section uses HTML markup, and contains a single widget zone without any layout or formatting.
You can replace the default section globally for the Email Builder to ensure that your editable areas have output code that exactly matches your email design requirements. Replacing the default section is required if you wish to use MJML markup for Email Builder components.
- Adjust the configuration of
EmailOptions
in your application’s startup code (Program.cs). - Assign the identifier of your preferred default section into the
DefaultSectionIdentifier
property. - To disable the system’s built-in Default section, also set the
RegisterDefaultSection
property tofalse
.
Warning: If your email channels already have existing emails that use Email Builder, replace all occurrences of the Default section in email content before you disable it. Otherwise, an error will occur when viewing or sending the given emails.
builder.Services.Configure<EmailBuilderOptions>(options =>
{
// Allows Email Builder for the 'Acme.Email' content type
options.AllowedEmailContentTypeNames = ["Acme.Email"];
// Sets the default Email Builder section
options.DefaultSectionIdentifier = "Acme.DefaultSection";
// Disables the system's default Email Builder section
options.RegisterDefaultSection = false;
});
Empty editable areas now use the new default section. The change does NOT modify the sections within existing Email Builder content.
You can also override the global default section for individual editable areas – specify the section identifier in the DefaultSectionIdentifier
property of EditableArea
components.
Section notes and limitations
Email Builder sections currently do not support configurable properties.
Widgets
Widgets are reusable components that represent individual pieces of email content, such as text, images, CTA links, etc. Content editors can easily add, configure and move widgets within widget zones in the Email Builder visual interface.
Develop widgets as ASP.NET Razor components and follow our Blazor development best practices.
Widgets with MJML markup
When using MJML markup for Email Builder components, do not include <mj-column> components in widget content. Columns must be defined in the layout of the parent sections.
Widget properties
When developing Email Builder widgets, you can define properties that allow content editors to adjust the widget content or behavior directly in the administration interface. Users interact with widget properties through the widget configuration dialog.
Use the following process to develop properties for a widget:
Create a model class implementing the
IEmailWidgetProperties
interface.- We recommend storing the properties model class within the same folder and namespace as the related widget’s Razor component files.
Define each widget property by adding a corresponding C# property in the model class.
- You can set default values for the properties.
Assign editing components to define the visual interface for properties that you want to make editable in the widget’s configuration dialog.
C#Example - Widget property modelusing Kentico.EmailBuilder.Web.Mvc; using Kentico.Xperience.Admin.Base.FormAnnotations; public class CTAWithTextWidgetProperties : IEmailWidgetProperties { // Property providing a text input for the main CTA caption [TextInputComponent(Label = "CTA caption", Order = 1)] public string Caption { get; set; } // Property providing a text input for the CTA link URL [TextInputComponent(Label = "CTA URL", Order = 2)] public string Url { get; set; } // Property providing a rich text editor for an intro text block [RichTextEditorComponent(Label = "CTA intro text", Order = 3)] public string RichTextIntro { get; set; } }
Custom editing components with links to content items
If you decorate any widget property with a custom form component that allows users to add links to content items (other than the default combined content selector and rich text editor), you need to create a custom reference extractor in order to enable usage tracking in the widget property.Advanced property options
You can add dynamic visibility conditions and validation that restricts how and when properties are displayed in the widget configuration dialog.Render or otherwise handle the properties in the widget’s code.
- The widget’s Razor component code must contain a property that:
- is named Properties
- has a type matching the widget’s property model class
- is designated as a razor component property via the
Parameter
attribute
- Note: Always consider the type of content that you are rendering in the widget markup. For example, properties using the RichTextEditorComponent editing component return raw HTML values, which you need to render as a MarkupString.
C#Example - Handling widget properties<mj-button href="@Properties?.Url" background-color="#f45e43" color="white"> @Properties?.Caption </mj-button> <mj-text> @((MarkupString)Properties?.RichTextIntro) </mj-text> @code { [Parameter] public CTAWithTextWidgetProperties Properties { get; set; } }
- The widget’s Razor component code must contain a property that:
Set the
PropertiesType
property of theRegisterEmailWidget
attribute to the type of the property model class when registering the widget.
Users can now click the Configure () icon when working with widgets in the Email Builder UI. This opens the widget properties dialog, and the configured property values affect the content of the widget in the resulting email.
Register widgets
Widgets must be registered using the RegisterEmailWidget
assembly attribute. You can add the attribute to the start of the widget’s Razor component code-behind class (if you have created one), or use a central class to register all of your Email Builder components, e.g., ComponentRegister.cs
.
Specify the following parameters for the attribute:
identifier
– a unique identifier for the widget. We recommend using a unique prefix in your identifiers to prevent conflicts with other widgets, for example matching your company’s name.name
– the name displayed in the widget selection dialog within the Email Builder UI.componentType
– theSystem.Type
of the widget’s Razor component class.
For widgets with properties, you also need to specify the type of the widget’s property model class in the PropertiesType
property of the RegisterEmailWidget
attribute.
You can also set the following optional properties:
Description
– additional description text displayed as a tooltip in the Email Builder widget selection dialog.IconClass
– the font icon class displayed when viewing the widget in the widget list.
using Kentico.EmailBuilder.Web.Mvc;
[assembly: RegisterEmailWidget(
identifier: "Acme.CTAWithTextWidget",
name: "CTA with text",
componentType: typeof(CTAWithTextWidget),
PropertiesType = typeof(CTAWithTextWidgetProperties),
IconClass = "icon-light-bulb")]
Once your widgets are registered, they appear in the selection dialog displayed when adding widgets in the Email Builder UI.
Access contextual information in components
In some cases, you may need to adjust the content or functionality of your components according to the context in which they are used.
Email Builder mode
The Email Builder mode allow you to check the stage of the email lifecycle where a component is being used. For example, a component can be displayed to content editors in the Email Builder’s editing mode, or processed by the system when putting together the final email content sent to recipients.
Obtain an instance of the IEmailContextAccessor
service (using dependency injection), call the GetContext
method to retrieve an EmailContext
object, and access the BuilderMode
property. The property has a value from the EmailBuilderMode
enum with the following possible states:
Off
– the email’s output is being rendered before being sent to recipients. Also used when displaying emails in Preview mode.Edit
– the email is being edited in Email Builder.ReadOnly
– the email is being viewed in Email Builder, but editing is disabled (e.g., for sent or scheduled emails, or for users without the Update permission for the email channel).
@using Kentico.EmailBuilder.Web.Mvc
@inject IEmailContextAccessor emailContextAccessor
@if (emailContextAccessor.GetContext().BuilderMode == EmailBuilderMode.Edit)
{
@* Content rendered only in Email Builder edit mode *@
}
using Kentico.EmailBuilder.Web.Mvc;
// Stores an instance of the IEmailContextAccessor service (e.g., obtained using dependency injection)
private readonly IEmailContextAccessor emailContextAccessor;
// Executes the following block only in Email Builder edit mode
if (emailContextAccessor.GetContext().BuilderMode == EmailBuilderMode.Edit)
{
// Targeted custom logic
}
Email context
To get information about the specific email where a component is being used, obtain an instance of the IEmailContextAccessor
service (using dependency injection) and call its GetContext
method. This returns an EmailContext
object, with properties holding information about the current email:
LanguageName
– the language of the email containing the widget.EmailChannelId
– the ID of the email channel.ContentTypeName
– the code name of the content type used for the email containing the widget.