Use the Email Builder Starter Kit in your project
Email Builder in Xperience by Kentico empowers non-technical users to define the appearance of emails with a friendly drag-and-drop interface. Through Templates, Sections, and Widgets, marketers have more flexibility than standard email templates to control the structure, visual design, and content of emails.
The Email Builder Starter Kit provides a pre-made set of Email Builder components that you can use in your project, saving time that you can spend on additional custom components or other areas of the project.
Let’s look into the process of integrating the Starter Kit into an existing project, using the Training guides repository as an example.
Before you start
This guide requires the following:
- Familiarity with C#, .NET Core, Dependency injection, and the MVC pattern.
- A running instance of Xperience by Kentico, preferably 30.5.0 or higher.
Some features covered in the Training guides may not work in older versions.
Code samples
You can find a project with completed, working versions of code samples from this guide and others in the finished branch of the Training guides repository.
The main branch of the repository provides a starting point to code along with the guides.
The code samples in this guide have only been tested in .NET 8.
They come from a project that uses implicit using directives. You may need to add additional using
directives to your code if your project does not use this feature.
Prepare your solution for Email Builder
To begin, install the Kentico.Xperience.Mjml and Microsoft.AspNetCore.Components.Web NuGet packages to your solution.
Make sure to install the version of Kentico.Xperience.Mjml that matches your other Kentico NuGet packages (30.5.0 in this example).
The process may vary depending on your approach, but in the Training guides repository, you can simply edit the Directory.Packages.props file.
<Project>
...
<ItemGroup>
...
<PackageVersion Include="kentico.xperience.mjml" Version="30.5.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="8.0.14" />
...
Copy the Starter Kit to your solution
Clone or download the Email Builder Starter Kit repository from GitHub.
Copy the entire Kentico.Xperience.Mjml.StarterKit.Rcl directory from the src folder of the Email Builder Starter Kit repository to your project, for example, into the src folder of the Training guides repository. Remember to include the Starter Kit’s project, Kentico.Xperience.Mjml.StarterKit.Rcl.csproj, in your solution. Your development environment likely has a way to do this in the UI, and it is also possible through the .NET CLI:
dotnet sln TrainingGuides.sln add Kentico.Xperience.Mjml.StarterKit.Rcl\Kentico.Xperience.Mjml.StarterKit.Rcl.csproj
Depending on the configuration of your project and environment, you may need to modify the Starter Kit’s project file. For the Training guides solution, we need to disable default embedded resources to avoid build errors
...
<PropertyGroup>
...
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
</PropertyGroup>
...
Map the Starter Kit’s models to your content types
The Starter Kit repository includes widgets that display images and products.
However, there is no way to guarantee that these entities are represented by identical content types in every project, so we need to create a mapping between the models used by the Starter Kit widgets and our project’s content types.
The Starter Kit repository contains example mappers for the Dancing Goat project for reference.
Implement the model mappers
In order to use the Image and Product widgets, we need to implement the IComponentModelMapper<TModel>
interface for the ImageWidgetModel
and ProductWidgetModel
types. These implementations tell the Starter Kit how to populate the widget models from our content types.
In case we need to update and overwrite the Email Builder Starter Kit files in the future, let’s minimize any changes to that project. Otherwise, we might lose them when overwriting or replacing the folder.
In the TrainingGuides.Web project, create a ~/Features/Shared/EmailBuilder/ModelMappers folder to hold our implementations.
Image model mapper (simple content type)
The Training guides repo has two reusable content types that primarily represent images: Asset and Gallery image.
However, the Gallery image type simply wraps the Asset type with additional properties. The image file associated with a Gallery image always belongs to the Asset it links, so we don’t need to worry about handling gallery images here.
You can also map multiple content types to a single Starter Kit model, but it is not necessary for this example.
using Kentico.Xperience.Mjml.StarterKit.Rcl.Mapping;
using Kentico.Xperience.Mjml.StarterKit.Rcl.Widgets;
using TrainingGuides.Web.Features.Shared.Services;
namespace TrainingGuides.Web.Features.Shared.EmailBuilder.ModelMappers;
public class ImageEmailWidgetModelMapper : IComponentModelMapper<ImageWidgetModel>
{
private readonly IContentItemRetrieverService<Asset> assetRetrieverService;
public ImageEmailWidgetModelMapper(
IContentItemRetrieverService<Asset> assetRetrieverService)
{
this.assetRetrieverService = assetRetrieverService;
}
public async Task<ImageWidgetModel> Map(Guid itemGuid, string languageName)
{
var asset = await assetRetrieverService.RetrieveContentItemByGuid(
contentItemGuid: itemGuid,
contentTypeName: Asset.CONTENT_TYPE_NAME,
languageName: languageName);
if (asset is null)
{
return new ImageWidgetModel();
}
string imageUrl = asset.AssetFile?.Url ?? string.Empty;
return new ImageWidgetModel()
{
// Populate the image URL and alt text from the retrieved content item's fields
ImageUrl = imageUrl,
AltText = asset?.AssetAltText ?? string.Empty
};
}
}
Product model mapper (composable content type)
The ProductPage content type in the Training Guides repo is composable. It wraps a reusable Product content type, which itself is made up of other content types like Product feature, Benefit, and Asset. If we compare the Product content type with the properties of the ProductWidgetViewModel from the Starter Kit, we can see that Product and Asset are the only parts of ProductPage that we need.
When you query the product page, remember to set the depth
appropriately, in order to handle this content model.
using Kentico.Xperience.Mjml.StarterKit.Rcl.Mapping;
using Kentico.Xperience.Mjml.StarterKit.Rcl.Widgets;
using TrainingGuides.Web.Features.Shared.Services;
namespace TrainingGuides.Web.Features.Shared.EmailBuilder.ModelMappers;
public class ProductEmailWidgetModelMapper : IComponentModelMapper<ProductWidgetModel>
{
private readonly IContentItemRetrieverService<ProductPage> productPageRetrieverService;
public ProductEmailWidgetModelMapper(
IContentItemRetrieverService<ProductPage> productPageRetrieverService)
{
this.productPageRetrieverService = productPageRetrieverService;
}
// Here we set a very specific name for the Guid parameter to clarify that the web page's ContentItemGuid should be used instead of WebPageItemGuid
public async Task<ProductWidgetModel> Map(Guid webPageItemContentItemGuid, string languageName)
{
var page = await productPageRetrieverService.RetrieveWebPageByContentItemGuid(
contentItemGuid: webPageItemContentItemGuid,
contentTypeName: ProductPage.CONTENT_TYPE_NAME,
depth: 2,
languageName: languageName);
var product = page?.ProductPageProduct.FirstOrDefault();
// If the product or page is null, return an empty model. Note the product will always be null if the page is null.
if (product is null)
{
return new ProductWidgetModel();
}
string webPageItemUrl = page.GetUrl().AbsoluteUrl;
var image = product.ProductMedia.FirstOrDefault();
string imageUrl = image?.AssetFile?.Url ?? string.Empty;
return new ProductWidgetModel
{
Name = product.ProductName,
Description = product.ProductDescription,
Url = webPageItemUrl,
ImageUrl = imageUrl,
ImageAltText = image?.AssetAltText ?? image?.AssetDescription ?? string.Empty,
};
}
}
Register the mappers
Now that the mappers are ready, let’s register them with the dependency injection container.
In our example, we can simply add them to the AddTrainingGuidesServices
method of the ServiceCollectionExtensions
class.
using Kentico.Xperience.Mjml.StarterKit.Rcl.Mapping;
using Kentico.Xperience.Mjml.StarterKit.Rcl.Widgets;
using TrainingGuides.Web.Features.Shared.EmailBuilder.ModelMappers;
...
namespace TrainingGuides.Web;
public static class ServiceCollectionExtensions
{
public static void AddTrainingGuidesServices(this IServiceCollection services)
{
...
services.AddScoped<IComponentModelMapper<ImageWidgetModel>, ImageEmailWidgetModelMapper>();
services.AddScoped<IComponentModelMapper<ProductWidgetModel>, ProductEmailWidgetModelMapper>();
...
}
...
}
The Program.cs file calls this method on startup, so the application will register both mappers.
Set up the Starter Kit and Email Builder
Register the MJML Starter Kit template
By default, the Starter Kit does not register its included page template. This gives you the opportunity to specify which of your content types should have access to it, or easily leave it out.
Create a new file in the ~/Features/Shared/EmailBuilder folder of the TrainingGuides.Web project called StarterKitComponentRegister.cs
using Kentico.EmailBuilder.Web.Mvc;
using Kentico.Xperience.Mjml.StarterKit.Rcl.Templates;
using TrainingGuides;
[assembly: RegisterEmailTemplate(
identifier: EmailBuilderStarterKitTemplate.IDENTIFIER,
name: "MJML Starter Kit template",
componentType: typeof(EmailBuilderStarterKitTemplate),
// Enter the code names of all email content types where you wish to use Email Builder
ContentTypeNames = [BasicEmail.CONTENT_TYPE_NAME])]
Enable Email Builder
If you run your project now, you’ll notice that you can’t use the MJML Starter Kit template for emails even though you just registered it. This is because you haven’t yet enabled Email Builder.
During startup, we need to enable Email Builder and configure its options. You need to specify which content types should use Email Builder at startup. You can also override the default section used in Editable areas on email templates.
Let’s enable Email marketing and Email Builder for the Basic email content type and set the default section to the Full-width section from the Starter Kit.
...
using Kentico.EmailBuilder.Web.Mvc;
using Kentico.Xperience.Mjml;
...
builder.Services.AddKentico(async features =>
{
...
features.UseEmailMarketing();
features.UseEmailBuilder();
});
...
builder.Services.Configure<EmailBuilderOptions>(options =>
{
options.AllowedEmailContentTypeNames = [BasicEmail.CONTENT_TYPE_NAME];
// Replaces the default Email Builder section
options.RegisterDefaultSection = false;
options.DefaultSectionIdentifier = FullWidthEmailSection.IDENTIFIER;
});
builder.Services.AddMjmlForEmails();
...
Configure the Starter Kit
Similar to the Email Builder configuration, you also need to configure the Starter Kit when the application starts.
The available options allow you to define the path to a stylesheet that Email Builder will use, and the allowed content types to restrict the selections of the Image and Product widgets.
For our example, let’s use styles from the example included in the Starter Kit, and restrict the widget selectors to the content types we already mapped to the widget models.
Copy the stylesheet
Since email CSS is beyond the scope of this example, let’s just copy the Dancing Goat stylesheet for Email Builder from the Starter Kit repository.
In the Starter Kit repo, find the EmailBuilder.css file under the examples/DancingGoat/wwwroot folder and copy it to the wwwroot folder of your TrainingGuides.Web project.
Now, you can reference this file in the Starter Kit configuration.
Configure the Starter Kit
Returning to the Program.cs file, use the AddKenticoMjmlStarterKit
method to configure the Starter Kit’s options. You need to tell it where to find your Email Builder CSS and which content types the Image and Product selectors can use.
...
using Kentico.Xperience.Mjml.StarterKit.Rcl;
using Kentico.Xperience.Mjml.StarterKit.Rcl.Sections;
...
builder.Services.AddKenticoMjmlStarterKit(options =>
{
options.StyleSheetPath = "EmailBuilder.css";
// The Asset content type, which we mapped to the Image widget earlier
options.AllowedImageContentTypes = [Asset.CONTENT_TYPE_NAME];
// The Product page content type, which we mapped to the Product widget earlier.
options.AllowedProductContentTypes = [ProductPage.CONTENT_TYPE_NAME];
});
...
If you prefer to configure these values in your appsettings.json file, you can call builder.Services.AddKenticoMjmlStarterKit(builder.Configuration);
instead.
Then you can set the options under the MjmlStarterKitOptions
object in appsettings.json.
See the results
Now the Starter Kit is integrated into your project.
If you’ve been following along with the Training guides example, you can see the results by creating a new email in the Training guides emails channel.
Explore further
To see more examples of Email Builder components in action, we recommend checking out those used on the Community Portal.
Here, you can see the code that makes up community.kentico.com, our site for the Xperience by Kentico community that features many helpful resources.
What’s next?
Continue on to the next guide in this series, to walk through the construction of a custom Article widget in line with the examples from the Starter Kit.