NPv.Mail
2.2.0
dotnet add package NPv.Mail --version 2.2.0
NuGet\Install-Package NPv.Mail -Version 2.2.0
<PackageReference Include="NPv.Mail" Version="2.2.0" />
<PackageVersion Include="NPv.Mail" Version="2.2.0" />
<PackageReference Include="NPv.Mail" />
paket add NPv.Mail --version 2.2.0
#r "nuget: NPv.Mail, 2.2.0"
#:package NPv.Mail@2.2.0
#addin nuget:?package=NPv.Mail&version=2.2.0
#tool nuget:?package=NPv.Mail&version=2.2.0
NPv.Mail
SMTP delivery and Scriban-based email template rendering for NPv.Mail.Abstractions.
π Breaking changes
v2.0.0
- Target framework updated to
net10.0(droppednet9.0support).
This is a personal library that focuses on the latest .NET runtime to keep maintenance simple and enjoyable.
β¨ What this package provides
NPv.Mail is the infrastructure implementation package for the mail contracts from NPv.Mail.Abstractions.
- π§
MailKitMailSenderβ sends HTML email through SMTP using MailKit and MimeKit. - βοΈ Default SMTP settings via
IOptions<SmtpSettings>β bind one application-level SMTP configuration fromIConfiguration. - π Per-call SMTP override β send a specific message with explicit
SmtpSettings, useful for tenant-specific or customer-specific sender accounts. - π Attachments β attach files with file name, byte content, and MIME content type.
- π
ScribanEmailTemplateRendererβ renders localized embedded HTML templates with Scriban. - π§± Built-in layout support β wraps rendered template content in a localized
Layout.html/Layout-{language}.jsonlayout. - π Clean architecture friendly β depends on abstractions and is easy to register in DI.
Use this package in your application or infrastructure layer when you need concrete mail delivery and template rendering.
π¦ Installation
dotnet add package NPv.Mail
The package already references NPv.Mail.Abstractions, so you usually do not need to install it separately unless another project only needs the contracts.
βοΈ Configuration
Add SMTP settings to appsettings.json:
{
"Smtp": {
"ServerName": "smtp.example.com",
"ServerPort": 465,
"UserName": "user@example.com",
"Password": "yourpassword",
"FromAddress": "noreply@example.com",
"FromName": "Example Sender"
}
}
MailKitMailSender connects with SecureSocketOptions.SslOnConnect, so configure the port expected by your SMTP provider for SSL-on-connect connections.
π Register services
using NPv.Mail.Abstractions.Sending;
using NPv.Mail.Abstractions.Templating;
using NPv.Mail.Sending;
using NPv.Mail.Templating;
builder.Services.Configure<SmtpSettings>(
builder.Configuration.GetSection("Smtp"));
builder.Services.AddTransient<IMailSender, MailKitMailSender>();
builder.Services.AddTransient<IEmailTemplateRenderer>(_ =>
new ScribanEmailTemplateRenderer([typeof(Program).Assembly]));
Pass every assembly that contains your embedded email templates to ScribanEmailTemplateRenderer. The renderer also falls back to the model assembly for model-specific templates and to the NPv.Mail assembly for the built-in layout.
π§ Send email with default SMTP settings
Use this overload when the message should be delivered through the SMTP settings bound at application startup.
var sender = app.Services.GetRequiredService<IMailSender>();
var message = new MailRequest
{
To = "recipient@example.com",
Subject = "Welcome!",
HtmlBody = "<p>Hello world!</p>",
Attachments =
[
new MailAttachment
{
FileName = "hello.txt",
Content = Encoding.UTF8.GetBytes("Hello world!"),
ContentType = "text/plain"
}
]
};
await sender.SendAsync(message, CancellationToken.None);
π Send email with explicit SMTP settings
Use this overload when SMTP settings must be selected dynamically, for example for a specific tenant, customer, mailbox, or one-off delivery configuration.
var smtpSettings = new SmtpSettings
{
ServerName = "smtp.customer.example.com",
ServerPort = 465,
UserName = "customer-sender@example.com",
Password = "smtp-password",
FromAddress = "customer-sender@example.com",
FromName = "Customer Sender"
};
var message = new MailRequest
{
To = "recipient@example.com",
Subject = "Welcome!",
HtmlBody = "<p>Hello from a custom SMTP account.</p>"
};
await sender.SendAsync(smtpSettings, message, CancellationToken.None);
π Render localized templates
ScribanEmailTemplateRenderer renders an IEmailTemplateModel by convention:
- HTML template:
{ModelTypeName}.html - localization file:
{ModelTypeName}-{LanguageCode}.json - layout template:
Layout.html - layout localization file:
Layout-{LanguageCode}.json
The model must implement IEmailTemplateModel and provide LanguageCode.
public sealed record ConfirmEmailTemplateModel(
string ConfirmUrl,
string LanguageCode) : IEmailTemplateModel;
Example embedded template files:
EmailTemplates/
ConfirmEmailTemplateModel.html
ConfirmEmailTemplateModel-en.json
ConfirmEmailTemplateModel-en.json can contain localized strings and Scriban expressions that use model properties:
{
"subject": "Confirm your email",
"title": "Welcome!",
"buttonText": "Confirm email",
"buttonUrl": "{{ ConfirmUrl }}"
}
ConfirmEmailTemplateModel.html can use both model properties and localized values:
<h1>{{ title }}</h1>
<p>Please confirm your email address.</p>
<a href="{{ buttonUrl }}">{{ buttonText }}</a>
Render the template:
var renderer = app.Services.GetRequiredService<IEmailTemplateRenderer>();
var content = renderer.Render(
new ConfirmEmailTemplateModel(confirmUrl, "en"));
Console.WriteLine(content.Subject);
Console.WriteLine(content.HtmlBody);
The returned EmailContent contains the rendered subject and final HTML body after the content template has been inserted into the layout.
π§© Main contracts used by this package
public interface IMailSender
{
Task SendAsync(SmtpSettings smtpSettings, MailRequest message, CancellationToken cancellationToken);
Task SendAsync(MailRequest message, CancellationToken cancellationToken);
}
public interface IEmailTemplateRenderer
{
EmailContent Render<T>(T model) where T : IEmailTemplateModel;
}
π Related packages
NPv.Mail.Abstractionsβ contracts and DTOsNPv.DataAccess.Efβ EF Core repository infrastructureNPv.DataAccess.Dapper.Executorβ raw SQL execution via Dapper
Author's Note
This library grew out of my long-standing personal interest in structuring and publishing open source packages. Over time, Iβve revisited and refined earlier internal utilities and ideas, giving them a more consistent shape and preparing them for wider reuse. Along the way, Iβve also taken the opportunity to explore how open source distribution and licensing work in the .NET ecosystem.
Itβs a small step toward something Iβve always wanted to try β sharing practical, minimal tools that reflect years of learning, experimentation, and refinement.
Hopefully, someone finds it useful.
Nikolai π
βοΈ License
MIT β free for commercial and open-source use.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- MailKit (>= 4.17.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.9)
- MimeKit (>= 4.17.0)
- NPv.Mail.Abstractions (>= 2.2.0)
- Scriban (>= 7.2.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.