
One of the last blog posts (Text and EMail Templates with Handlebars.NET ) was generally about HandleBars.NET - a very popular template engine. This post is about how you can easily implement layouts with Handlebars.
The snippet in the previous post was as follows:
1// MyTemplateProvider.cs
2
3 public MyTemplateProvider()
4 {
5 _handlebars = Handlebars.Create();
6
7 Init(); // Add this
8 }
9
10 private void Init()
11 {
12 // Add Header Template
13 _handlebars.RegisterTemplate("Header_Template", @"<strong>MyCompany - {{Title}}</strong>");
14
15 // Add Footer Template
16 _handlebars.RegisterTemplate("Footer_Template", @"<i>MyCompany - Copyright 2023</i>");
17
18 // Register Welcome Mail
19 _handlebars.RegisterTemplate("WelcomeMail_Template",
20 """
21 <div>
22 <h1>{{>Header_Template}}</h1>
23 <div class="body">
24 <h3>Welcome to our demo {{UserName}}!</h3>
25 <p>Thank you for registering. Have fun!</p>
26 </div>
27 <footer>{{>Footer_Template}}</footer>
28 </div>
29 """);
30 }
As you can see, a template is registered that references other templates - but explicitly. I therefore have to take care of the frame - in this case the header and footer - for my actual content myself. This is not nice and represents a redundancy.
Partial Blocks
Handlebars supports a functionality called Partial Blocks. While simple Partials represent a content block, partial blocks are the construct around it. In Razor, this is comparable to sections. Partial blocks can therefore be used to represent the surrounding content - like our layouts.
1_handlebars.RegisterTemplate("MailLayout", """
2 <div>
3 <h1><strong>MyCompany - {{Title}}</strong></h1>
4 <div class="body">
5 {{> content}}
6 </div>
7 <footer><i>MyCompany - Copyright {{On.Year}}</i></footer>
8 </div>
9 """);
Partial blocks are also registered as templates, but have the special feature that they contain the reserved partial keyword content. This is then replaced by the actual content you have in your template.
The layout can then be referenced in the actual template by specifying the partial block:
1_handlebars.RegisterTemplate("WelcomeMail_Template",
2 """{{#> MailLayout}}
3 <h3>Welcome to our demo {{UserName}}!</h3>
4 <p>Thank you for registering. Have fun!</p>
5 {{/MailLayout}}
6 """);
Full Sample
1using HandlebarsDotNet;
2
3namespace BenjaminAbt.Samples.HandlebarsNET;
4
5internal class Program
6{
7 static void Main(string[] args)
8 {
9 MyTemplateProvider templateProvider = new();
10
11 List<string> userNames = ["Batman", "Robin"];
12 foreach (var userName in userNames)
13 {
14 string text = templateProvider.RenderWelcomeMail(userName);
15
16 Console.WriteLine($"To: {userName}");
17 Console.WriteLine(text);
18 Console.WriteLine("-------------------");
19 }
20 }
21}
22
23// MyTemplateProvider.cs
24public class MyTemplateProvider
25{
26 private readonly IHandlebars _handlebars;
27
28 public MyTemplateProvider()
29 {
30 _handlebars = Handlebars.Create();
31
32 Init(); // Add this
33 }
34
35 private void Init()
36 {
37
38 _handlebars.RegisterTemplate("MailLayout", """
39 <div>
40 <h1><strong>MyCompany - {{Title}}</strong></h1>
41 <div class="body">
42 {{> content}}
43 </div>
44 <footer><i>MyCompany - Copyright {{On.Year}}</i></footer>
45 </div>
46 """);
47
48 // Register Welcome Mail
49 _handlebars.RegisterTemplate("WelcomeMail_Template", InternalRegisterTemplateWithLayout(
50 """
51 <h3>Welcome to our demo {{UserName}}!</h3>
52 <p>Thank you for registering. Have fun!</p>
53 """));
54 }
55
56 private string InternalRegisterTemplateWithLayout(string template)
57 => "{{#> MailLayout}}" + template + "{{/MailLayout}}";
58
59 private HandlebarsTemplate<object, object>? _welcomeTemplate = null;
60 public string RenderWelcomeMail(string userName)
61 {
62 _welcomeTemplate ??= _handlebars.Compile("{{> WelcomeMail_Template}}");
63
64 var data = new
65 {
66 Title = "Welcome!",
67 UserName = userName,
68 On = DateTimeOffset.UtcNow
69 };
70
71 var text = _welcomeTemplate(data);
72 return text;
73 }
74}
Have fun!
Related articles

Oct 05, 2026 - 12 min read
Problem Details in ASP.NET Core and .NET 11
builder.Services.AddProblemDetails() registers infrastructure. It does not create an error contract. An ASP.NET Core request can fail during …

Sep 21, 2026 - 11 min read
Resilience in .NET with Microsoft.Extensions.Resilience
Retries are the most visible resilience strategy and the easiest one to misuse. Repeating a failing call can recover from a short network …

Sep 14, 2026 - 10 min read
What Matters in ASP.NET Core 11
ASP.NET Core 11 has no single feature that fundamentally changes how web applications are built. That is not a criticism. Mature frameworks …
Let's Work Together
Looking for an experienced Platform Architect or Engineer for your next project? Whether it's cloud migration, platform modernization or building new solutions from scratch - I'm here to help you succeed.
