Text and EMail Templates with Handlebars.NET

Text and EMail Templates with Handlebars.NET

A frequent requirement in apps is the conversion of templates, e.g. for sending e-mails, for PDF templates or other text modules. There are many different ways to do this, ranging from simple string replacements to the Razor engine of ASP.NET Core.

In this article I would like to introduce another possibility: Handlebars.NET.

HandleBars

Handlebars.NET is a library for the .NET platform that enables the use of Handlebars templates in .NET applications. Handlebars is a simple and expressive templating language that allows dynamic content to be inserted into static templates. The Handlebars.NET library provides a .NET implementation of this language.

  • Templating syntax: Handlebars.NET allows templates to be defined using a simple and more readable syntax. Placeholders and control structures are represented by curly brackets and hash characters.

  • Data binding: The library enables data to be bound to templates. This means that dynamic content from a data source can be inserted into the template. This is particularly useful for creating HTML pages with variable content.

  • Extensibility: Handlebars.NET supports the extension of functionality through the implementation of custom helper functions and block helpers. Developers can add their own functions to integrate complex logic into templates.

  • Platform independence: Because it is a .NET library, Handlebars.NET can be used in various .NET environments, including ASP.NET applications and console applications.

HandleBars.NET is an open source project on GitHub, which is referenced via a NuGet package. Please consider sponsoring the project if you use it for commercial purposes.

Sample

In principle, HandleBars offers various options for rendering text, ranging from simple scenarios to more complex requirements, including custom render modules.

One of the best features of HandleBars is that templates can be registered; and an instance is required for registration. In addition to a global instance, however, isolated instances are better in real implementations - because this way I cannot collide with other elements in my app.

It therefore makes sense to create a separate class that manages the instance and registers the templates.

// MyTemplateProvider.cs

public class MyTemplateProvider
{
    private readonly IHandlebars _handlebars;

    public MyTemplateProvider()
    {
        _handlebars = Handlebars.Create();
    }

I can now add certain templates to this instance and register them with the instance.

// MyTemplateProvider.cs

    public MyTemplateProvider()
    {
        _handlebars = Handlebars.Create();

        Init(); // Add this
    }

    private void Init()
    {
        // Add Header Template
        _handlebars.RegisterTemplate("Header_Template", @"<strong>MyCompany - {{Title}}</strong>");

        // Add Footer Template
        _handlebars.RegisterTemplate("Footer_Template", @"<i>MyCompany - Copyright 2023</i>");

        // Register Welcome Mail
        _handlebars.RegisterTemplate("WelcomeMail_Template",
            """
            <div>
                <h1>{{>Header_Template}}</h1>
                <div class="body">
                    <h3>Welcome to our demo {{UserName}}!</h3>
                <p>Thank you for registering. Have fun!</p>
                </div>
                <footer>{{>Footer_Template}}</footer>
            </div>
            """);
    }

What is still missing is the actual rendering, i.e. transferring values to the template. To do this, the template must first be compiled, and the compilation can then be used to inject values. HandleBars.NET offers the Compile method for this, so that we can only compile the template itself once for performance and resource reasons and then use it again and again.

// MyTemplateProvider.cs

    private HandlebarsTemplate<object, object>? _welcomeTemplate = null;
    public string RenderWelcomeMail(string userName)
    {
        _welcomeTemplate ??= _handlebars.Compile("{{>WelcomeMail_Template}}");

        var data = new
        {
            Title = "Welcome!",
            UserName = userName
        };

        var text = _welcomeTemplate(data);
        return text;
    }

The renderer is now ready and can be used in an application in a very simple and high-performance way.

internal class Program
{
    static void Main(string[] args)
    {
        MyTemplateProvider templateProvider = new();

        List<string> userNames = ["Batman", "Robin"];
        foreach (var userName in userNames)
        {
            string text = templateProvider.RenderWelcomeMail(userName);

            Console.WriteLine($"To: {userName}");
            Console.WriteLine(text);
            Console.WriteLine("-------------------");
        }
    }
}

Output:

To: Batman
<div>
  <h1><strong>MyCompany - Welcome!</strong></h1>
  <div class="body">
     <h3>Welcome to our demo Batman!</h3>
    <p>Thank you for registering. Have fun!</p>
  </div>
  <footer><i>MyCompany - Copyright 2023</i></footer>
</div>
-------------------
To: Robin
<div>
  <h1><strong>MyCompany - Welcome!</strong></h1>
  <div class="body">
     <h3>Welcome to our demo Robin!</h3>
    <p>Thank you for registering. Have fun!</p>
  </div>
  <footer><i>MyCompany - Copyright 2023</i></footer>
</div>
-------------------