Love developing emails

TL;DR

Using React Email and Azure Functions can simplify email templating, improve reusability, and provide a maintainable and scalable solution for managing and sending emails in your application.

Here’s a sample repo to get started: github.com/desholmes/react-emails-azure-functions.

Strike fear into the heart of any developer…

We need to develop some emails for our application 😶

Nooooo! 🤢

Ask any developer and they will tell you developing emails is like being transported back in time to the dark ages of web development. It’s a world where tables reign supreme, inline styles are the norm, and compatibility issues are rife (IE5 anyone?).

Email templating is a crucial part of modern applications, whether for transactional emails, marketing campaigns, or user notifications. Traditional approaches often involve hand-cracking HTML templates using tables and inline styles, leading to poor maintainability.

In this post, I’ll walk-through a stack that simplifies email templating, improves reusability, streamlines QA and provides a more maintainable solution for sending emails in your application.

React Email

React Email is a very refreshing approach to email templating, offering plenty of benefits:

  1. Component-based structure: Easily reuse and compose email UI elements
  2. Server-side rendering (SSR) for emails: Ensures compatibility with all major email clients
  3. TypeScript support: Enhances type safety and developer experience
  4. Consistent styling: Use TailwindCSS or inline styles effectively
  5. Send test emails: Preview emails in your browser & send from a local live reloading server

So, you focus on writing clean, maintainable code and let React Email handle the complexities of email rendering. 🥳

That’s the templating sorted, but where to send the emails from?

Azure Functions

I’m a huge fan of Azure Functions, they’re managed, serverless and scalable, and I think they’re a perfect fit for queuing, scheduling, and sending emails.

Sample Repo: React Email + Azure Functions

Overview

The sample repo focuses on processing messages from the queue, rendering and sending emails using Resend (other email providers can be used).

Overview

From bottom to top:

  1. An event is triggered* (e.g. user registration)
  2. A message is generated and added to the queue
  3. A queue trigger function processes the message, rendering the email and sending it

*The sample repo doesn’t include an example trigger, but you can either push the message directly into the queue (using @azure/storage-queue), or you could add a HTTP trigger function to receive the message and add bindings to add the message to the queue. It really depends on your architecture & preference.

React Email Template

React Email provides all of the basic components you need, the sample repo includes a simple HelloWorld:

import {
  Body,
  Container,
  Head,
  Heading,
  Html,
  Link,
  Preview,
  Section,
  Text,
} from "@react-email/components";
import * as React from "react";

export const HelloWorld = () => (
  <Html>
    <Head />
    <Preview>Hello There!</Preview>
    <Body style={main}>
      <Container style={container}>
        <Section style={logoContainer}>A Logo Here</Section>
        <Heading style={h1}>A Title</Heading>
        <Text style={heroText}>I am a helpful, short, introduction.</Text>

        <Section style={codeBox}>
          <Text style={confirmationCodeText}>Hello World!</Text>
        </Section>

        <Text style={text}>More text could be added under here too.</Text>
        <Section>
          <Link
            style={footerLink}
            href="https://dholmes.co.uk/blog"
            target="_blank"
            rel="noopener noreferrer"
          >
            Des Holmes Blog
          </Link>
          &nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;
          <Link
            style={footerLink}
            href="https://github.com/desholmes"
            target="_blank"
            rel="noopener noreferrer"
          >
            Des Holmes GitHub
          </Link>
          &nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;
          <Link
            style={footerLink}
            href="https://react.email/"
            target="_blank"
            rel="noopener noreferrer"
          >
            React Email
          </Link>
          &nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;
          <Link
            style={footerLink}
            href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-overview"
            target="_blank"
            rel="noopener noreferrer"
            data-auth="NotApplicable"
            data-linkindex="6"
          >
            Azure Functions
          </Link>
          <Text style={footerText}>&copy;2025 Some company, 1234 Some Street, Some City, Some Country</Text>
        </Section>
      </Container>
    </Body>
  </Html>
);

export default HelloWorld;

const footerText = {
  fontSize: "12px",
  color: "#b7b7b7",
  lineHeight: "15px",
  textAlign: "left" as const,
  marginBottom: "50px",
};

const footerLink = {
  color: "#b7b7b7",
  textDecoration: "underline",
};

const main = {
  backgroundColor: "#ffffff",
  margin: "0 auto",
  fontFamily:
    "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
};

const container = {
  margin: "0 auto",
  padding: "0px 20px",
};

const logoContainer = {
  marginTop: "32px",
  backgroundColor: "#000",
  color: "#fff",
  height: "80px",
  padding: "10px",
  textAlign: "center" as const,
  textTransform: "uppercase" as const,
  fontSize: "32px",
  fontWeight: "700",
};

const h1 = {
  color: "#1d1c1d",
  fontSize: "36px",
  fontWeight: "700",
  margin: "30px 0",
  padding: "0",
  lineHeight: "42px",
};

const heroText = {
  fontSize: "20px",
  lineHeight: "28px",
  marginBottom: "30px",
};

const codeBox = {
  background: "rgb(245, 244, 245)",
  borderRadius: "4px",
  marginBottom: "30px",
  padding: "40px 10px",
};

const confirmationCodeText = {
  fontSize: "30px",
  textAlign: "center" as const,
  verticalAlign: "middle",
};

const text = {
  color: "#000",
  fontSize: "14px",
  lineHeight: "24px",
};

And the local preview environment allow you to:

  1. View desktop and mobile previews
  2. View the generated email code
  3. Send a test email

Email preview

Azure Function

The sample repo includes a simple queue triggered function qTSendEmail. This function processes the message, renders the email and sends it using Resend.

import { AzureFunction, Context } from "@azure/functions";
import { Resend } from "resend";
import { render } from "@react-email/components";
import { RESEND_API_KEY, RESEND_FROM_EMAIL } from "../constants";
import { getEmailTemplate, templateNameExists } from "../utils";

interface EmailMessage {
  templateName: string;
  to: string;
  subject: string;
}

const qTSendEmail: AzureFunction = async function (
  context: Context,
  emailMessage: EmailMessage
): Promise<void> {
  const { templateName, to, subject } = emailMessage;
  if (!templateNameExists(templateName)) {
    context.log.error(`Invalid Template Name: ${templateName}`);
    return;
  }

  try {
    const emailHtml = await render(getEmailTemplate(templateName));
    const resend = new Resend(RESEND_API_KEY);
    resend.emails.send({
      to: to,
      from: RESEND_FROM_EMAIL,
      subject: subject,
      html: emailHtml,
    });
  } catch (error) {
    context.log.error(`qTSendEmail: Failed with error`, error);
  }
};

export default qTSendEmail;

Improvements

The sample repo includes the bare-bones to show the approach, but there are plenty of improvements to be made:

QA Preview

For QA you can add fixtures & host the preview environment on a server for easy access (blog post to follow using Docker), allowing you to perform cross-client testing without needing to trigger the emails from your application.

Dynamic Data

Also the Azure Function could be extended to include dynamic data. For example, you could pass a data object in the message and use it to populate the email template.

Wrapping Up

Developing emails doesn’t have to be hell. Using React Emails and Azure Functions can simplify email templating, improve reusability, and provide a more maintainable solution for sending emails in your application.


Related Posts