ASP.NET Core  

Building a Dynamic Email Template Engine With Handlebars or Razor in ASP.NET Core

This article explains how to build a fully dynamic email template engine that supports placeholders, conditional blocks, reusable layouts, variables, and preview mode.
We will cover Handlebars (client-friendly) and Razor (powerful server-side) approaches.

Sections include practical code, database scripts, workflow diagrams, ER diagram, architecture diagram, and a sequence diagram — all in smaller, professional heading styles.

1. Introduction

Modern applications often require dynamic emails such as password reset, invoice notifications, shipping alerts, onboarding sequences, and marketing campaigns.
Hard-coded email bodies quickly become difficult to maintain.

A template engine allows you to:

  • Create emails without deploying code

  • Insert placeholders such as {{UserName}} or {{TotalAmount}}

  • Support layouts, partials, looping, conditions

  • Preview email in UI

  • Send test emails

  • Manage template versions

2. Requirements for a Modern Email Template Engine

A practical enterprise-grade email system must support:

  • Dynamic placeholders

  • Conditional rendering

  • Table loops

  • Templates stored in database

  • Preview and test message

  • Lightweight DSL such as Handlebars or Razor

  • Variable validation

  • HTML + plain text output

  • Attachments (optional)

3. Database Design

Email Templates Table

ColumnTypeDescription
TemplateIdINT (PK)Unique ID
TemplateNameVARCHAR(200)Key such as "OrderConfirmation"
SubjectTemplateNVARCHAR(MAX)Subject with placeholders
BodyTemplateNVARCHAR(MAX)HTML body
CreatedDateDATETIME
UpdatedDateDATETIME
IsActiveBIT

SQL Script

CREATE TABLE EmailTemplate (
    TemplateId INT IDENTITY(1,1) PRIMARY KEY,
    TemplateName VARCHAR(200) NOT NULL,
    SubjectTemplate NVARCHAR(MAX) NOT NULL,
    BodyTemplate NVARCHAR(MAX) NOT NULL,
    CreatedDate DATETIME DEFAULT GETDATE(),
    UpdatedDate DATETIME NULL,
    IsActive BIT DEFAULT 1
);

4. ER Diagram (Simplified)

+-------------------+
| EmailTemplate     |
+-------------------+
| TemplateId (PK)   |
| TemplateName      |
| SubjectTemplate   |
| BodyTemplate      |
| CreatedDate       |
| UpdatedDate       |
| IsActive          |
+-------------------+

5. Architecture Diagram (Visio Style)

+-----------------+        +-------------------+
| Angular UI      | <----> |  Email API        |
| (Template Editor)|        |  (ASP.NET Core)   |
+-----------------+        +--------+----------+
                                   |
                                   |
                            +------v---------+
                            | SQL Server     |
                            | Templates      |
                            +----------------+
                                   |
                                   |
                           +-------v--------+
                           | Email Sender   |
                           | (SMTP/SendGrid)|
                           +----------------+

6. Workflow / Flowchart (Template Rendering)

[User Requests Email] 
        |
        v
[Load Template by Name]
        |
        v
[Inject Variables]
        |
        v
[Render Using Handlebars/Razor]
        |
        v
[Send Email via SMTP]

7. Using Handlebars in ASP.NET Core

Step 1: Install Package

dotnet add package Handlebars.Net

Step 2: Template Rendering Service

public class HandlebarsTemplateService
{
    public string Render(string template, object data)
    {
        var compiled = Handlebars.Compile(template);
        return compiled(data);
    }
}

Step 3: Example Template

Hello {{UserName}},  
Your order {{OrderId}} of amount {{Total}} has been confirmed.

Step 4: Rendering Example

var model = new
{
    UserName = "Rajesh",
    OrderId = "SO-4521",
    Total = 5500
};

string output = service.Render(templateBody, model);

8. Using Razor as Template Engine

Razor gives more power:

  • Conditions

  • Loops

  • Inline C#

Step 1: Install package

dotnet add package RazorLight

Step 2: Razor Rendering Service

public class RazorTemplateService
{
    private readonly RazorLightEngine engine;

    public RazorTemplateService()
    {
        engine = new RazorLightEngineBuilder()
            .UseMemoryCaching()
            .Build();
    }

    public async Task<string> RenderAsync(string template, object model)
    {
        return await engine.CompileRenderStringAsync(Guid.NewGuid().ToString(), template, model);
    }
}

Step 3: Example Razor Email

Hi @Model.UserName,
@if(Model.IsPremium)
{
    <p>Thank you for being a premium member!</p>
}
<p>Your order total: @Model.Total</p>

9. API Endpoints (ASP.NET Core)

Get All Templates

[HttpGet]
public IActionResult GetTemplates()
{
    return Ok(_repository.GetAll());
}

Render Preview

[HttpPost("preview")]
public async Task<IActionResult> Preview([FromBody] PreviewRequest request)
{
    var template = _repo.GetByName(request.TemplateName);
    var result = await _templateService.RenderAsync(template.BodyTemplate, request.Data);
    return Ok(result);
}

10. Angular Module (Template Editor UI)

Routes

const routes: Routes = [
  { path: '', component: TemplateListComponent },
  { path: 'edit/:id', component: TemplateEditorComponent },
  { path: 'preview/:id', component: TemplatePreviewComponent }
];

Template Editor (HTML)

<textarea [(ngModel)]="template.bodyTemplate" rows="20"></textarea>

<button (click)="preview()">Preview</button>
<button (click)="save()">Save</button>

Preview Component

<div [innerHTML]="previewHtml"></div>

11. Sequence Diagram (Rendering & Sending Email)

User
  |
  | Request → /email/send
  v
Email API
  | Load Template
  | Inject Variables
  | Render (Handlebars/Razor)
  v
SMTP Service
  | Send Email
  v
User (Email Delivered)

12. Adding Layouts & Partials

For enterprise projects, common header/footer must be reusable.

Handlebars Example

{{> header}}
Hello {{UserName}}
{{> footer}}

Register partial

Handlebars.RegisterTemplate("header", "<h2>Welcome</h2>");

13. Versioning Support

Add a new table:

TemplateVersionId INT PK
TemplateId INT FK
VersionNo INT
BodyTemplate NVARCHAR(MAX)
CreatedDate DATETIME

14. Production-Grade Enhancements

  • Template variable validator

  • Multi-language support

  • WYSIWYG editor for non-tech users

  • Embedded images

  • Dark/light mode emails

  • Import/export templates

15. Conclusion

A dynamic email template engine dramatically improves maintainability and makes your system scalable. Using Handlebars gives simplicity, while Razor provides power and flexibility.

With ASP.NET Core + Angular + SQL Server, you can build a fully professional template engine with preview, versioning, layouts, and dynamic placeholders.