Whether 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
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
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
Nodemailer for sending emails
npm install nodemailer
Required types
npm install --save-dev @types/nodemailer
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
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
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
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: [],
};
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
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>© {new Date().getFullYear()} Your Company. All rights reserved.</p>
</div>
</Container>
</Body>
</Html>
);
};
export default EmailLayout;
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);
}
}
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
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);
}
}
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
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!' };
}
}
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 {}
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 {}
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
Use a tool like Postman or curl to send the request:
POST http://localhost:3000/mail/send-welcome
{
"email": "recipient@example.com"
}
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
Top comments (0)