OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

How to Ensure Email Template Compatibility Across Different Email Clients, I am using Nodemailer, ejs in a Node.js Application?

  • Thread starter Thread starter Manjunath Naik
  • Start date Start date
M

Manjunath Naik

Guest
Context: I'm working on a Node.js backend application using the MERN stack with TypeScript. I use Nodemailer for sending emails, and I initially used EJS for my email templates. However, I encountered formatting issues in various email clients (e.g., Gmail displays correctly, but web.de and GMX do not).

Code:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ticket Returned to LoCo</title>
    <style>
        body {
            min-width: 100;
            font-family: Arial, Helvetica, sans-serif;
            font-size: 16px;
            line-height: 1.5;
            color: #222222;
        }
        .container {
            max-width: 600px;
        }
        p {
            color: #000000;
        }
        .message {
            white-space: pre-line;
        }
    </style>
</head>
<body>
    <div class="container">
        <% if (message) { %>
            <p class="message"><%= message %></p>
        <% } %>
        <% if (signature) { %>
            <img src="<%= signature %>" alt="Signature" style="max-width: 60%;">
        <% } %>
    </div>
</body>
</html>

import nodemailer, { Transporter } from 'nodemailer';
import ejs from 'ejs';
import path from 'path';
import juice from 'juice';

type Attachment = string | Express.Multer.File;

export interface EmailOptions {
    email: string | undefined;
    subject: string;
    template: string;
    data: { [key: string]: any };
    attachments?: Attachment[];
    cc: string[];
    bcc: string[];
}

export interface TransportOptions {
    host: string;
    port: number;
    service: string;
    secure: boolean;
    auth: {
        user: string;
        pass: string;
    };
}

const sendMail = async (options: EmailOptions, transportOptions?: TransportOptions): Promise<string> => {
    const transporter: Transporter = nodemailer.createTransport(transportOptions || {
        host: process.env.SMTP_HOST,
        port: parseInt(process.env.SMTP_PORT!),
        service: process.env.SMTP_SERVICE,
        secure: process.env.SMTP_SECURE === 'false',
        auth: {
            user: process.env.SMTP_MAIL,
            pass: process.env.SMTP_PASSWORD
        },
    });

    const { email, subject, template, data, attachments, cc, bcc } = options;
    
    const templatePath = path.join(__dirname, "../template/mails", template);
    const templatePathOptional = path.join(__dirname, "../template/mails", "locomail.ejs");

    const html: string = await ejs.renderFile(templatePath, data);
    const inlinedHtml = juice(html);

    console.log("juice-html", inlinedHtml);
    
    const htmlOptional: string = await ejs.renderFile(templatePathOptional, { email, subject, data, cc, bcc });

    const mailOptions = {
        from: process.env.SMTP_MAIL,
        to: email,
        cc: cc,
        bcc: bcc,
        subject,
        html: inlinedHtml,
        attachments: attachments ? attachments.map(attachment => typeof attachment === 'string' ? { path: attachment } : {
            filename: attachment.originalname,
            content: attachment.buffer,
            contentType: attachment.mimetype
        }) : []
    };

    await transporter.sendMail(mailOptions);
    return htmlOptional;
};

export default sendMail;

<p>Context:
I'm working on a Node.js backend application using the MERN stack with TypeScript. I use Nodemailer for sending emails, and I initially used EJS for my email templates. However, I encountered formatting issues in various email clients (e.g., Gmail displays correctly, but web.de and GMX do not).</p>
<pre><code><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ticket Returned to LoCo</title>
<style>
body {
min-width: 100;
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
line-height: 1.5;
color: #222222;
}
.container {
max-width: 600px;
}
p {
color: #000000;
}
.message {
white-space: pre-line;
}
</style>
</head>
<body>
<div class="container">
<% if (message) { %>
<p class="message"><%= message %></p>
<% } %>
<% if (signature) { %>
<img src="<%= signature %>" alt="Signature" style="max-width: 60%;">
<% } %>
</div>
</body>
</html>

import nodemailer, { Transporter } from 'nodemailer';
import ejs from 'ejs';
import path from 'path';
import juice from 'juice';

type Attachment = string | Express.Multer.File;

export interface EmailOptions {
email: string | undefined;
subject: string;
template: string;
data: { [key: string]: any };
attachments?: Attachment[];
cc: string[];
bcc: string[];
}

export interface TransportOptions {
host: string;
port: number;
service: string;
secure: boolean;
auth: {
user: string;
pass: string;
};
}

const sendMail = async (options: EmailOptions, transportOptions?: TransportOptions): Promise<string> => {
const transporter: Transporter = nodemailer.createTransport(transportOptions || {
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT!),
service: process.env.SMTP_SERVICE,
secure: process.env.SMTP_SECURE === 'false',
auth: {
user: process.env.SMTP_MAIL,
pass: process.env.SMTP_PASSWORD
},
});

const { email, subject, template, data, attachments, cc, bcc } = options;

const templatePath = path.join(__dirname, "../template/mails", template);
const templatePathOptional = path.join(__dirname, "../template/mails", "locomail.ejs");

const html: string = await ejs.renderFile(templatePath, data);
const inlinedHtml = juice(html);

console.log("juice-html", inlinedHtml);

const htmlOptional: string = await ejs.renderFile(templatePathOptional, { email, subject, data, cc, bcc });

const mailOptions = {
from: process.env.SMTP_MAIL,
to: email,
cc: cc,
bcc: bcc,
subject,
html: inlinedHtml,
attachments: attachments ? attachments.map(attachment => typeof attachment === 'string' ? { path: attachment } : {
filename: attachment.originalname,
content: attachment.buffer,
contentType: attachment.mimetype
}) : []
};

await transporter.sendMail(mailOptions);
return htmlOptional;
};

export default sendMail;
</code></pre>
 

Latest posts

Top