DEV Community

Boyinbode Ebenezer Ayomide
Boyinbode Ebenezer Ayomide

Posted on

Building A Scalable Advanced Email Templating System with @react-email and NestJS

Image descriptionWhether you're sending transactional emails or personalized messages, having a templating system in place can greatly enhance the efficiency and flexibility of your email handling process. In this tutorial, we will explore how to build an email templating system in NestJS using @react-email for building dynamic and visually appealing email templates.

This integration will allow you to leverage the power of React's components for email design while NestJS will handle the backend logic to send these emails. Let’s walk through the steps with sample code to make the process easier.

Why Use @react-email/components?
@react-email/components provides a powerful and flexible way to create reusable email templates. With React's component-driven architecture, you can break down your email layout into small, manageable pieces and use the same code for the frontend and backend. This library abstracts the complexity of HTML emails, which can be tedious to manage manually.

Prerequisites
Before we begin, ensure you have the following installed:

Node.js (v14 or later)
NestJS CLI
TypeScript
Enter fullscreen mode Exit fullscreen mode

A basic understanding of React
Step 1: Set Up a New NestJS Project
Start by setting up a new NestJS project if you don't already have one.

$ nest new email-templating
$ cd email-templating
Enter fullscreen mode Exit fullscreen mode

Once your project is set up, we will install the necessary packages.

Step 2: Install Required Dependencies
Next, install the required packages for email templating and sending.

React Email components
npm install @react-email/components react react-dom
Enter fullscreen mode Exit fullscreen mode

Nodemailer for sending emails

npm install nodemailer
Enter fullscreen mode Exit fullscreen mode

Required types

npm install --save-dev @types/nodemailer

Enter fullscreen mode Exit fullscreen mode

Step 3: Create Email Templates Using @react-email/components
Now, let’s create a simple email template using @react-email/components. First, create a new directory called email-templates inside the src folder.

mkdir src/email-templates
Enter fullscreen mode Exit fullscreen mode

To include Tailwind CSS for styling and a reusable layout wrapper for your emails, you’ll need to set up a global layout component that can be used across different email templates, while leveraging Tailwind's utility classes for styling.

$ npm install tailwindcss

Enter fullscreen mode Exit fullscreen mode

Inside the email-templates folder, create a file called welcome-email.tsx. This will be our first email template.

Now, configure Tailwind CSS by creating a tailwind.config.js file in your root directory.

$ npx tailwindcss init
Enter fullscreen mode Exit fullscreen mode

Next, we need to configure Tailwind to process styles within the email templates. Add the following content inside your tailwind.config.js file:

module.exports = {
  content: [
    './src/email-templates/**/*.{js,ts,jsx,tsx}',
    './src/layout/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};
Enter fullscreen mode Exit fullscreen mode

To wrap every email with a consistent layout and styling, create a Layout component in your src/layout directory. This layout will serve as the base for all emails.

$ mkdir src/layout
$ touch src/layout/EmailLayout.tsx
Enter fullscreen mode Exit fullscreen mode

Here’s the EmailLayout component that uses Tailwind for styling:

// src/layout/EmailLayout.tsx
import React from 'react';
import { Html, Head, Body, Container } from '@react-email/components';

const EmailLayout = ({ children }: { children: React.ReactNode }) => {
  return (
    <Html>
      <Head />
      <Body className="bg-gray-100 p-6">
        <Container className="max-w-xl mx-auto bg-white p-6 shadow-md rounded-lg">
          {/* Header */}
          <div className="text-center mb-8">
            <h1 className="text-2xl font-semibold text-indigo-600">Your Company</h1>
          </div>

          {/* Email Content */}
          {children}

          {/* Footer */}
          <div className="text-center mt-10 text-gray-500 text-sm">
            <p>&copy; {new Date().getFullYear()} Your Company. All rights reserved.</p>
          </div>
        </Container>
      </Body>
    </Html>
  );
};

export default EmailLayout;

Enter fullscreen mode Exit fullscreen mode

This layout provides a structured, reusable container for all emails with Tailwind’s utility classes. It wraps the content in a clean layout, including a header and footer, ensuring consistency across all emails.

// src/mail/mail.service.ts
import { Injectable } from '@nestjs/common';
import nodemailer from 'nodemailer';
import { render } from '@react-email/components';
import WelcomeEmail from '../email-templates/welcome-email';

@Injectable()
export class MailService {
  private transporter;

  constructor() {
    this.transporter = nodemailer.createTransport({
      service: 'Gmail',
      auth: {
        user: 'your-email@gmail.com',
        pass: 'your-email-password',
      },
    });
  }

  async sendWelcomeEmail(to: string) {
    const emailHtml = render(<WelcomeEmail />);

    const mailOptions = {
      from: 'your-email@gmail.com',
      to,
      subject: 'Welcome to Our Service!',
      html: emailHtml,
    };

    await this.transporter.sendMail(mailOptions);
  }
}

Enter fullscreen mode Exit fullscreen mode

Step 4: Set Up Email Sending in NestJS
Now that we have the template ready, let's integrate it with the backend to send emails. We'll be using nodemailer for email delivery.

First, create a mail.service.ts file in the src/mail directory. This service will handle sending emails using nodemailer.

mkdir src/mail
touch src/mail/mail.service.ts
Enter fullscreen mode Exit fullscreen mode

Here’s how you can set up the mail service:


// src/mail/mail.service.ts
import { Injectable } from '@nestjs/common';
import nodemailer from 'nodemailer';
import { render } from '@react-email/components';
import WelcomeEmail from '../email-templates/welcome-email';

@Injectable()
export class MailService {
  private transporter;

  constructor() {
    this.transporter = nodemailer.createTransport({
      service: 'Gmail',
      auth: {
        user: 'your-email@gmail.com',
        pass: 'your-email-password',
      },
    });
  }

  async sendWelcomeEmail(to: string) {
    const emailHtml = render(<WelcomeEmail />);

    const mailOptions = {
      from: 'your-email@gmail.com',
      to,
      subject: 'Welcome to Our Service!',
      html: emailHtml,
    };

    await this.transporter.sendMail(mailOptions);
  }
}
Enter fullscreen mode Exit fullscreen mode

In this service, we set up nodemailer with Gmail as the transport service. Replace the authentication details with your own email credentials. The sendWelcomeEmail method renders the React component to HTML using the render method from @react-email/components and sends it as an email.

Step 5: Create a Mail Controller
To trigger the email sending from an endpoint, create a new controller called mail.controller.ts.

touch src/mail/mail.controller.ts
Enter fullscreen mode Exit fullscreen mode

Here’s how you can set up the controller:

// src/mail/mail.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { MailService } from './mail.service';

@Controller('mail')
export class MailController {
  constructor(private readonly mailService: MailService) {}

  @Post('send-welcome')
  async sendWelcomeEmail(@Body('email') email: string) {
    await this.mailService.sendWelcomeEmail(email);
    return { message: 'Welcome email sent successfully!' };
  }
}
Enter fullscreen mode Exit fullscreen mode

This controller exposes a POST /mail/send-welcome endpoint that takes an email address as the body parameter and triggers the sendWelcomeEmail method in the MailService.

Step 6: Set Up the Mail Module
Next, we’ll set up a MailModule to register both the MailService and MailController.

// src/mail/mail.module.ts
import { Module } from '@nestjs/common';
import { MailService } from './mail.service';
import { MailController } from './mail.controller';

@Module({
  providers: [MailService],
  controllers: [MailController],
})
export class MailModule {}
Enter fullscreen mode Exit fullscreen mode

Finally, import the MailModule in your main application module.

// src/app.module.ts
import { Module } from '@nestjs/common';
import { MailModule } from './mail/mail.module';

@Module({
  imports: [MailModule],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Step 7: Test the Integration
Now, you can test the email sending functionality by running your NestJS application and making a POST request to the /mail/send-welcome endpoint with a valid email address in the request body.

npm run start:dev
Enter fullscreen mode Exit fullscreen mode

Use a tool like Postman or curl to send the request:

POST http://localhost:3000/mail/send-welcome
{
  "email": "recipient@example.com"
}
Enter fullscreen mode Exit fullscreen mode

If everything is set up correctly, you should receive the welcome email in the specified inbox.

Feel free to expand this by adding more dynamic content to your templates, handling different types of emails (password resets, notifications), and exploring advanced features of nodemailer.

Happy coding!

Let's Connect

Linkedln | Github | Twitter | Youtube

Top comments (0)