Documentation Index
Fetch the complete documentation index at: https://docs.aspfox.com/llms.txt
Use this file to discover all available pages before exploring further.
Where templates live
All email templates are in src/Acme.Infrastructure/Email/Templates/. Each template is a C# class that extends EmailTemplateBase.
src/Acme.Infrastructure/Email/Templates/
├── EmailTemplateBase.cs ← base class with HTML scaffolding
├── WelcomeTemplate.cs
├── EmailVerificationTemplate.cs
├── PasswordResetTemplate.cs
├── MagicLinkTemplate.cs
├── PasswordChangedTemplate.cs
├── TenantInvitationTemplate.cs
├── TrialExpiry7DayTemplate.cs
├── TrialExpiry1DayTemplate.cs
├── PaymentFailedTemplate.cs
├── PaymentRecoveredTemplate.cs
├── CancellationScheduledTemplate.cs
└── CancellationConfirmedTemplate.cs
How templates work
EmailTemplateBase provides:
- The outer HTML envelope (DOCTYPE,
<head>, brand color, inline styles)
- Header with the application name
- Footer with unsubscribe language
- Abstract
RenderContent() method that each template implements
Each template class fills in RenderContent() with its specific body HTML:
// EmailVerificationTemplate.cs
public class EmailVerificationTemplate : EmailTemplateBase
{
private readonly string _userName;
private readonly string _verificationUrl;
public EmailVerificationTemplate(string userName, string verificationUrl)
{
_userName = userName;
_verificationUrl = verificationUrl;
}
protected override string RenderContent()
{
return $"""
<h2 style="color: #1A56DB; font-size: 20px; margin: 0 0 16px;">
Verify your email address
</h2>
<p style="color: #374151; margin: 0 0 24px;">
Hi {_userName}, click the button below to verify your email address.
This link expires in 24 hours.
</p>
<a href="{_verificationUrl}"
style="background: #1A56DB; color: white; padding: 12px 24px;
text-decoration: none; border-radius: 6px; display: inline-block;">
Verify Email
</a>
""";
}
}
Changing the brand color
The primary color is set in one place in EmailTemplateBase:
// EmailTemplateBase.cs
private const string PrimaryColor = "#1A56DB";
Change this value and it propagates to every template header, button, and accent element.
Changing email copy
Find the relevant template class, edit the string in RenderContent(). The C# raw string literal ("""...""") makes multi-line HTML easy to write and read.
Adding a logo
Replace the text-based header in EmailTemplateBase.RenderHeader() with an <img> tag:
// Before:
private string RenderHeader() => $"""
<div style="background: {PrimaryColor}; padding: 24px; text-align: center;">
<span style="color: white; font-size: 24px; font-weight: 700;">Acme</span>
</div>
""";
// After (host your logo somewhere publicly accessible):
private string RenderHeader() => $"""
<div style="background: {PrimaryColor}; padding: 24px; text-align: center;">
<img src="https://cdn.yourdomain.com/logo-white.png"
alt="Acme" height="32" style="display: block; margin: 0 auto;" />
</div>
""";
Adding a new template
Step 1: Add a value to the EmailTemplate enum in Domain:
// src/Acme.Domain/Enums/EmailTemplate.cs
public enum EmailTemplate
{
// existing values...
WeeklyDigest,
}
Step 2: Create the template class:
// src/Acme.Infrastructure/Email/Templates/WeeklyDigestTemplate.cs
public class WeeklyDigestTemplate : EmailTemplateBase
{
private readonly string _userName;
private readonly int _newProjects;
public WeeklyDigestTemplate(string userName, int newProjects)
{
_userName = userName;
_newProjects = newProjects;
}
protected override string RenderContent()
{
return $"""
<h2 style="color: #1A56DB; font-size: 20px; margin: 0 0 16px;">
Your weekly digest
</h2>
<p style="color: #374151; margin: 0 0 16px;">
Hi {_userName}, here's what happened this week:
</p>
<ul style="color: #374151;">
<li>{_newProjects} new project(s) created</li>
</ul>
""";
}
}
Step 3: Add a case in ResendEmailService.SendTemplateAsync:
// src/Acme.Infrastructure/Email/ResendEmailService.cs
EmailTemplate.WeeklyDigest => new WeeklyDigestTemplate(
model.UserName,
model.NewProjects
),
Testing email rendering locally
Dump a template to HTML and open it in a browser — no SMTP server needed:
// Scratch: add temporarily to any endpoint or test
var template = new WeeklyDigestTemplate("Jane", 3);
var html = template.Render();
await File.WriteAllTextAsync("/tmp/weekly-digest-preview.html", html);
Then open /tmp/weekly-digest-preview.html in your browser. Delete this code before committing.
For sends that actually went through, check the Emails tab in the Resend dashboard — it shows the full rendered HTML, delivery timestamp, and open/click events.