Use Razor Layouts in FluentEmail to reuse Headers and Footers

June 14, 2022
Written by
Néstor Campos
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by

Use Razor Layouts in FluentEmail to reuse Headers and Footers

You can quickly and intuitively send emails using the FluentEmail library for .NET, but once you send many different emails in your project, you have to be able to organize your email templates in a good way to reuse content and enforce consistency in terms of format and design. Luckily, Razor Layouts can help you reuse parts of your templates like headers and footers.

Prerequisites

You will need the following for your development environment:

You can find the source code of this tutorial in this GitHub repository.

Set up an SMTP server

To be able to send emails at scale, you'll need an email sending service like the SendGrid Email API, but for local development, you can use a fake SMTP server, or for this project, you will use smtp4dev, a powerful tool that allows you, through a local SMTP server, to send emails without actually sending emails to your clients.

Open your console and run this command to install smtp4dev as a global .NET tool:

dotnet tool install -g Rnwood.Smtp4dev

And then run it with the following command:

smtp4dev

In your console, you will see smtp4dev is running locally. The output will display the ports smtp4dev is using for the SMTP and IMAP protocols, and also what URL you can use to get to the web user interface, which in this case is https://localhost:5000 and https://localhost:5001. The interface will show the emails that would go out if hooked up to a real email service, so you can verify your emails were sent without actually sending them to end users.

Information from the console with the result of the execution of smtp4dev, which includes the url of the web to check the emails sent.

You'll also see the SMTP port used by default is port 25, and being on your own machine, the server hostname is localhost. You will need this port and hostname when configuring your mailing application.


Let the smtp4dev tool run for the remainder of this tutorial and open a new console for all future commands.

If you want to learn more about smtp4dev, you can see this article with a more detailed example.

Why use Layouts?

Many projects will start with only one email template, but as the project grows and the number of templates increases, the templates will become harder to maintain. You will need to maintain a similar design between your templates, and specifically, you'll want to share a header and footer to maintain consistency in your communications with your users.

Maintaining that consistency by modifying each email template separately is a complicated task because you will need to repeat the same code in several templates. You could modify a header of a template and easily forget to modify the same header in your other templates.

Instead of duplicating your header and footer in each template, you can use Razor Layouts to simplify this process by creating a single layout with your header and footer, while still rendering the content that is unique to your template in between. For example, a template for a product promotion only needs to have the Razor and HTML to render the promotion but will be placed in between the header and the footer coming from the layout.

In a previous tutorial, I explained how you can install the FluentEmail library and use it to send emails. In this tutorial, you'll skip those steps and start from a sample where FluentEmail is already installed with code to send two emails. Clone the following project using git:

git clone https://github.com/nescampos/tdv_razorlayouts.git

Next, open the project using the IDE of your choice. The default values should work fine for local development, however, feel free to change the highlighted values as you wish:


using FluentEmail.Core;
using FluentEmail.Core.Models;
using FluentEmail.Razor;
using FluentEmail.Smtp;
using SendGridFluentEmailSender;
using System.Net.Mail;

string emailServer = "localhost";
int emailPort = 25;
using SmtpClient smtpClient = new SmtpClient(emailServer, emailPort);
Email.DefaultRenderer = new RazorRenderer();
Email.DefaultSender = new SmtpSender(smtpClient);

IFluentEmail firstEmail = Email
    .From("sender@localhost")
    .To("recipient@localhost")
    .Subject("Test Email")
    .Tag("test")
    .UsingTemplateFromFile(
        "FirstTemplate.cshtml", 
        new FirstTemplateModel { Name = "Nestor Campos" }
    );

IFluentEmail secondEmail = Email
    .From("sender@localhost")
    .To("recipient@localhost")
    .Subject("Second Email")
    .Tag("test")
    .UsingTemplateFromFile(
        "SecondTemplate.cshtml", 
        new SecondTemplateModel 
        {  
            Title = "Welcome to our site", 
            Description = "We are sending some product discounts for you as our new customer", 
            ColumnOne = "This is our first discount", 
            ColumnTwo = "This is our second discount" 
        }
    );


SendResponse firstEmailResponse = firstEmail.Send();
SendResponse secondEmailResponse = secondEmail.Send();

Console.WriteLine(firstEmailResponse.Successful ? "First email queued" : "First email failed to send");
Console.WriteLine(secondEmailResponse.Successful ? "Second email queued" : "Second email failed to send");

Run the application from the console:

dotnet run

Let the smtp4dev tool run for the remainder of this tutorial and open a new console for all future commands.

Now, check the smtp4dev web portal (by default https://localhost:5001), which will show you the emails sent as if you received them in a real mailbox, with the corresponding design and texts:

Web view of smtp4dev with the list of emails initially sent through the project, with their corresponding designs.

As you can see, the project works properly, but if you review the Razor files (.cshtml extension) in detail, you will realize that both files have a large part of the code duplicated, something that can be complicated to manage when there are already many templates that you have to code and maintain. At this point, it is that the Layouts can facilitate this process.

smtp4dev is a helpful tool to test that your code sends emails correctly and that email contents are what you expect. However, do not use this tool to validate that your email looks and feels as expected. Every email inbox provider renders HTML emails slightly different, so when you're designing your HTML emails, you need to test the designs with each email inbox provider like Gmail, Outlook, Apple Mail, Yahoo Mail, etc.

Create a Layout File

Now, create a file in the root called _DefaultEmailLayout.cshtml. Next, add the following code to the file:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <meta name="x-apple-disable-message-reformatting">
  <title></title>
  <style>
    table, td, div, h1, p {font-family: Arial, sans-serif;}
  </style>
</head>
<body style="margin:0;padding:0;">
  <table role="presentation" style="width:100%;border-collapse:collapse;border:0;border-spacing:0;background:#ffffff;">
    <tr>
      <td align="center" style="padding:0;">
        <table role="presentation" style="width:602px;border-collapse:collapse;border:1px solid #cccccc;border-spacing:0;text-align:left;">
          <tr>
            <td align="center" style="padding:40px 0 30px 0;background:#70bbd9;">
              <img src="https://i.imgur.com/ntorKvb.png" alt="" width="300" style="height:auto;display:block;" />
            </td>
          </tr>
          <tr>
            @RenderBody()
          </tr>
          <tr>
            <td style="padding:30px;background:#ee4c50;">
              <table role="presentation" style="width:100%;border-collapse:collapse;border:0;border-spacing:0;font-size:9px;font-family:Arial,sans-serif;">
                <tr>
                  <td style="padding:0;width:50%;" align="left">
                    <p style="margin:0;font-size:14px;line-height:16px;font-family:Arial,sans-serif;color:#ffffff;">
                      &reg; Someone, Somewhere 2022<br/><a href="http://www.example.com" style="color:#ffffff;text-decoration:underline;">Unsubscribe</a>
                    </p>
                  </td>
                  <td style="padding:0;width:50%;" align="right">
                    <table role="presentation" style="border-collapse:collapse;border:0;border-spacing:0;">
                      <tr>
                        <td style="padding:0 0 0 10px;width:38px;">
                          <a href="http://www.twitter.com/" style="color:#ffffff;"><img src="https://i.imgur.com/PWtSlSu.png" alt="Twitter" width="38" style="height:auto;display:block;border:0;" /></a>
                        </td>
                        <td style="padding:0 0 0 10px;width:38px;">
                          <a href="http://www.facebook.com/" style="color:#ffffff;"><img src="https://i.imgur.com/zqdyPBl.png" alt="Facebook" width="38" style="height:auto;display:block;border:0;" /></a>
                        </td>
                      </tr>
                    </table>
                  </td>
                </tr>
              </table>
            </td>
          </tr>
        </table>
      </td>
    </tr>
  </table>
</body>
</html>

As you can see, you are creating a generic view with a header and footer shared by all emails, where the RenderBody() method in the Layout code specifies the section that will be replaced by the specific template you decide to use when sending an email.

The Razor Layout file does not need to start with an underscore (_) but it is a common naming convention in ASP.NET Core applied to any Razor file that isn't a "View". So the layout and partial Razor files are usually prefixed with an underscore. When using Razor outside of ASP.NET Core, it can be helpful to follow the same naming convention, so you can easily identify which Razor file is the actual email template and which Razor files are included in those templates.

Update email templates

Modify the first template

Open the FirstTemplate.cshtml file and replace all the content with the following code:

@model SendGridFluentEmailSender.FirstTemplateModel

@{
    Layout = "_DefaultEmailLayout.cshtml";
}

<td style="padding:36px 30px 42px 30px;">
    <table role="presentation" style="width:100%;border-collapse:collapse;border:0;border-spacing:0;">
    <tr>
        <td style="padding:0 0 36px 0;color:#153643;">
        <h1 style="font-size:24px;margin:0 0 20px 0;font-family:Arial,sans-serif;">This is the test email</h1>
        <p style="margin:0 0 12px 0;font-size:16px;line-height:24px;font-family:Arial,sans-serif;">This is a test email using <strong>Twilio SendGrid</strong> and <strong>FluentEmail</strong> created by @Model.Name</p>
        </td>
    </tr>
    </table>
</td>

What you have just done with this template is to specify the Layout that will be used for rendering all the code when generating and sending the email, and you adjusted the design of the specific content of this template.

Update the second template

Now, open the SecondTemplate.cshtml file, which will use the same Layout as the previous template. Updating with the following code in the file:

@model SendGridFluentEmailSender.SecondTemplateModel

@{
    Layout = "_DefaultEmailLayout.cshtml";
}

<td style="padding:36px 30px 42px 30px;">
    <table role="presentation" style="width:100%;border-collapse:collapse;border:0;border-spacing:0;">
    <tr>
        <td style="padding:0 0 36px 0;color:#153643;">
        <h1 style="font-size:24px;margin:0 0 20px 0;font-family:Arial,sans-serif;">@Model.Title</h1>
        <p style="margin:0 0 12px 0;font-size:16px;line-height:24px;font-family:Arial,sans-serif;">@Model.Description</p>
               
        </td>
    </tr>
    <tr>
        <td style="padding:0;">
        <table role="presentation" style="width:100%;border-collapse:collapse;border:0;border-spacing:0;">
            <tr>
            <td style="width:260px;padding:0;vertical-align:top;color:#153643;">
                <p style="margin:0 0 25px 0;font-size:16px;line-height:24px;font-family:Arial,sans-serif;"><img src="https://assets.codepen.io/210284/left.gif" alt="" width="260" style="height:auto;display:block;" /></p>
                <p style="margin:0 0 12px 0;font-size:16px;line-height:24px;font-family:Arial,sans-serif;">@Model.ColumnOne</p>
            </td>
            <td style="width:20px;padding:0;font-size:0;line-height:0;">&nbsp;</td>
            <td style="width:260px;padding:0;vertical-align:top;color:#153643;">
                <p style="margin:0 0 25px 0;font-size:16px;line-height:24px;font-family:Arial,sans-serif;"><img src="https://assets.codepen.io/210284/right.gif" alt="" width="260" style="height:auto;display:block;" /></p>
                <p style="margin:0 0 12px 0;font-size:16px;line-height:24px;font-family:Arial,sans-serif;">@Model.ColumnTwo</p>
            </td>
            </tr>
        </table>
        </td>
    </tr>
    </table>
</td>

This template uses the same Layout but with different content, which now gives you the flexibility to adjust only what you need, either the content of this template or the header and footer if you want a change that for all your emails.

After: Send emails with Razor Layouts

You de-duplicated the header and footer HTML by moving the HTML out of the individual email templates, into a layout file, and then referenced the layout from those individual templates. With all that done, run your project again:

dotnet run

To run the project in Visual Studio Code, press Ctrl + F5 to run.

In a few seconds, two emails will arrive in the recipient's mailbox, similar to the following:

Email sent to the recipient"s mailbox with the first template and layout applied for the design.

Email sent to the recipient"s mailbox with the second template and layout applied for the design.

Actually, the resulting HTML is the same emails from the first execution, but now they will be easier for you to manage, focusing only on what is important in each template or adjusting the entire design of all of them from the _DefaultEmailLayout.cshtml view.

Using SendGrid with FluentEmail

While using smtp4dev locally is very useful, when you deploy your application to production, you'll need to use a real email server. One option is to use the SendGrid Email API, which lets you send emails at scale while maximizing deliverability.  FluentEmail has built-in support for SendGrid. You can follow this tutorial to learn how to integrate SendGrid with FluentEmail.

Dynamic Email Templates from SendGrid

In addition to rendering email templates using Razor, an alternative solution is to use Dynamic Email Templates from SendGrid. Dynamic Email Templates will allow you to add your own designs or use designs available by default for your emails. You can create templates yourself through a graphical user interface or with code. These templates use the Handlebars templating language to create dynamic email templates and inject data into your emails. And just like the templates within the project, you can add your data to the template to customize the content delivered to each of your users.

Depending on your needs, storing the template in SendGrid instead of your source code could be beneficial, so you can update it at any time without having to redeploy your .NET application. Best of all, FluentEmail makes it easy to use these templates via the SendWithTemplateAsync() (source code) method.

Conclusion

Congratulations on making it to the end. 👏
You learned how to develop better Razor email templates by using Razor Layouts. I listed some additional resources below in case you want to learn more!

Additional resources

Check out the following resources for more information on the topics and tools presented in this tutorial:

FluentEmail repository – This repository contains all the information, classes, and methods to use FluentEmail.SendGrid library.

Layout in ASP.NET Core - More detailed explanation about Layouts in ASP.NET Core

Add FluentEmail to Dependency Injection – You can learn how to configure FluentEmail once into .NET's dependency injection container and use it throughout your apps.

How to send Emails in C# .NET with FluentEmail, Razor, and SendGrid

Handlebars at SendGrid - Learn how to use the Handlebars templating language for Dynamic Transactional Templates and Marketing Campaigns designs

Source Code to this tutorial on GitHub - You can find the source code for this project at this GitHub repository. Use it to compare solutions if you run into any issues.

 

Néstor Campos is a software engineer, tech founder, and Microsoft Most Valuable Professional (MVP), working on different types of projects, especially with Web applications.