HTML emails
editMeziantou.Framework.Templating.Html adds HtmlEmailTemplate, a template for the HTML body of an email. It uses {{ and }} as delimiters, which do not clash with HTML, and adds blocks that encode values, capture the subject of the message and list its embedded images.
Install the package
dotnet package add Meziantou.Framework.Templating.HtmlThe package depends on Meziantou.Framework.Templating, and its types are in the same namespace.
Write an email
using var template = new HtmlEmailTemplate();
template.Arguments.Add(new TemplateArgument("userName", typeof(string)));
template.Load("""
{{@begin_section title}}Welcome Email{{@end_section}}
<html>
<body>
<img src="{{cid logo.png}}" />
<h1>Hello {{#html userName}}!</h1>
</body>
</html>
""");
var result = template.Run(out var metadata,
new Dictionary<string, object?>(StringComparer.Ordinal) { ["userName"] = "John" });
Assert.NotNull(metadata);
Assert.Equal("Welcome Email", metadata.Title);
Assert.Equal("logo.png", Assert.Single(metadata.ContentIdentifiers));{{#html userName}}writes the value ofuserName, HTML-encoded.{{cid logo.png}}writes the URL of an image attached to the email, and addslogo.pngtometadata.ContentIdentifiers.- The
titlesection gives the subject of the email inmetadata.Title. Its content stays in the HTML. Run(out metadata, ...)returns the HTML, and the metadata through itsoutparameter. It takes the values of the arguments by name, as above, by position, or none. The overloads that take aTextWriterwrite the HTML to it.
Syntax
HtmlEmailTemplate supports every block of Template syntax, with {{ and }} instead of <% and %>, and adds the following ones:
| Block | Writes |
|---|---|
{{# expression }} | The value, not encoded, like {{= expression }} |
{{#html expression }} | The value, encoded for the text of an element |
{{#attr expression }} | The value, encoded for an attribute |
{{#url expression }} | The value, encoded for a part of a URL |
{{html statements }} | Nothing: runs C# statements written HTML-encoded (< for <) |
{{cid name }} | cid:name, the URL of an embedded image |
{{@begin_section name }} … {{@end_section }} | Nothing: the content between them is written and captured |
The keywords are case-sensitive and must be followed by a space: {{#htmlName}} is a # block that writes the variable htmlName without encoding it.
Use it in a server
HtmlEmailTemplate compiles to dynamic code unless OutputType is set, and a template that uses dynamic is never unloaded, even when disposed. An application that compiles templates as it runs, for instance one per tenant or on every change of a file, should set OutputType and give every argument a type:
using var template = new HtmlEmailTemplate { OutputType = typeof(HtmlEmailOutput) };
template.Arguments.Add(new TemplateArgument("userName", typeof(string)));
template.Load("Hello {{#html userName}}!");
var result = template.Run(out _, "<John>");
Assert.Equal("Hello <John>!", result);See Compilation and memory for the details.
Pages
- Encoding values: choose the encoding for each place of the HTML, and write code in an HTML editor.
- Subject and embedded images: capture the subject of the email and list the images to attach.