How to embed image using nodemailer

Nodemailer is a module offered by Node.js that sends emails from the server. It is easy to use and prioritizes user account security to offer a good and trustworthy experience. It requires a service to work on, e.g., Gmail, which we can specify in the transporter.has the configuration options that define how Nodemailer should send the emails.

What is an embedded image?

An embedded image is referred to the visuals directly included in the email's content rather than being attached separately. Recipients do not need to specially open or download an image to view it because it appears inline.

Embedded images improve the user experience and enhance the visual appearance of the email to make it more intriguing and easy to understand. They are preferred in domains where the visual content is priorities more than the text content, for example, marketing and promotions.

Visuals are more attractive than text.
Visuals are more attractive than text.

Implement using nodemailer

We can embed an image inside an email through nodemailer by attaching its content and adding HTML code to specify how the content will appear inside the email body. We can embed an image that exists as a local file on the system, or we can fetch an image through its URL and send that directly.

Note: Read the Answer on text-based email using nodemailer to better understand the concept well.

Using a downloaded image

In this example, we take an image that is already existing on our local system and get its exact path to locate where it exists. That image instance is then embedded in the email content. This requires that the image exists on the system in an accessible folder and exact image name is used in the code when defining it.

const express = require('express');
const bodyParser = require('body-parser');
const nodemailer = require('nodemailer');
const readline = require('readline');
const app = express();

//Create a readline interface
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

app.use(bodyParser.urlencoded({ extended: true }));

rl.question('Enter your Gmail email ID: ', (senderEmail) => {
  rl.question('Enter your password: ', (pass) => {
    rl.close();
    console.log('Server is running on port 8080');
    const transporter = nodemailer.createTransport({
     service: 'gmail',
     auth: {
      user: senderEmail,
      pass: pass,
     },
    });

    app.post('/submit', (req, res) => {
    const { email_, sub_, text_} = req.body;
    const mailOptions = {
      from: senderEmail,
      to: email_,
      subject: sub_,
      text: text_,
      html: `<div><p>${text_}</p><img src = "cid:myImg" style="width:400px;height:400px;"/></div>`,
      attachments: [{
        filename: 'Educative.png',
        path: __dirname + '/Educative.png',
        cid: 'myImg'
      }]
    };

    transporter.sendMail(mailOptions, (error, info) => {
      if (error) {
        console.log(error);
        res.send('<p>Error occurred while sending the email</p>');
      } else {
        console.log('Email sent: ' + info.response);
        res.send('<p>Email sent successfully!</p>');
      }
    });
   });
  });
});

app.get('/', (req, res) => {
  res.send(`
<form method="post" action="/submit" style="background-color: #D9F0FA; padding: 10px; border-radius: 5px;">
    <label for="email_" style="display: block; margin-bottom: 5px; font-weight: bold;">Email ID:</label>
    <input type="email" id="email_" name="email_" required style="width: 60%; padding: 8px; margin-bottom: 12px; border: 1px solid #ccc; border-radius: 3px;">
    <br>
    <label for="sub_" style="display: block; margin-bottom: 5px; font-weight: bold;">Subject Txt:</label>
    <input type="text" id="sub_" name="sub_" required style="width: 60%; padding: 8px; margin-bottom: 12px; border: 1px solid #ccc; border-radius: 3px;">
    <br>
    <label for="text_" style="display: block; margin-bottom: 5px; font-weight: bold;">Message:</label>
    <textarea id="text_" name="text_" required style="width: 60%; padding: 8px; margin-bottom: 12px; border: 1px solid #ccc; border-radius: 3px; resize: vertical; height: 150px;"></textarea>
    <br>
    <input type="submit" value="Submit" style="background-color: #194D63; color: #fff; padding:10px; border: none; border-radius: 4px;">
  </form>
  `);
});

app.listen(8080, () => {
});
Embedding image using downloaded image.

Code explanation

  • Line 27: Send a post request that is sent on the /submit endpoint when the submit button is clicked.

  • Line 28: Send the attributes taken as input in the form in the request body.

  • Line 29: Create an object mailOptions that contains the necessary email configurations.

  • Line 30-33: The object mailOptions consists of the necessary email configurations and sets the request body attributes on the corresponding attribute, including:

    • Recipient's email

    • The subject of the email

    • The text body of the email.

  • Line 34: Write the HTML content that appears in the email body inside the temporal literals.

  • Line 35-38: Create an attachments object and initialize the following instances in it:

    • filename which is the exact name of the local file we want to embed.

    • path which saves the exact path of the file that is saved locally. We use _dirname to fetch the exact path.

    • cid which is a unique instance containing the name of the file with which the file is referred in the HTML code.

Note: Read the explanation of the remaining code in the text-based email using nodemailer example.

Code output

The following output will be obtained once the code executes successfully. We can see an embedded image with a customized text body and an option to download.

Using an imageUrl

Now let's embed an image using its URL instead of passing an image locally. In this example, we take an image URL and fetch the content using axios and cors. That image instance is then embedded in the email content. This saves the user from the constraint of having a pre-existing locally saved image to send via nodemailer.

const express = require('express');
const bodyParser = require('body-parser');
const nodemailer = require('nodemailer');
const readline = require('readline');
const axios = require('axios');
const cors = require('cors');

const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

app.use(bodyParser.urlencoded({ extended: true }));
rl.question('Enter your Gmail email ID: ', (senderEmail) => {
  rl.question('Enter your password: ', (pass) => {
    rl.close();
    console.log('Server is running on port 8080');
    const transporter = nodemailer.createTransport({
      service: 'gmail',
      auth: {
        user: senderEmail,
        pass: pass,
      },
    });

    app.post('/submit', async (req, res) => {
      const { email_, sub_, text_ } = req.body;
      //Give image URL
      const imageUrl = 'https://a.fsdn.com/allura/s/educative/icon?53ff56ed6e68e5c15d81a4cc59970aa50998e2684d8c5f4d960061d317ef3389?&w=90';
      const response = await axios.get(imageUrl, { responseType: 'arraybuffer' });
      const imageBuffer = Buffer.from(response.data, 'binary');
      const mailOptions = {
        from: senderEmail,
        to: email_,
        subject: sub_,
        text: text_,
        html: `<div><p>${text_}</p><img src="cid:myImg" style="width:200px;height:200px;"/></div>`,
        attachments: [
          {
            content: imageBuffer,
            cid: 'myImg',
          },
        ],
      };

      transporter.sendMail(mailOptions, (error, info) => {
        if (error) {
          console.log(error);
          res.send('<p>Error occurred while sending the email</p>');
        } else {
          console.log('Email sent: ' + info.response);
          res.send('<p>Email sent successfully!</p>');
        }
      });
    });
  });
});

app.get('/', (req, res) => {
  res.send(`
<form method="post" action="/submit" style="background-color: #D9F0FA; padding: 10px; border-radius: 5px;">
    <label for="email_" style="display: block; margin-bottom: 5px; font-weight: bold;">Email ID:</label>
    <input type="email" id="email_" name="email_" required style="width: 60%; padding: 8px; margin-bottom: 12px; border: 1px solid #ccc; border-radius: 3px;">
    <br>
    <label for="sub_" style="display: block; margin-bottom: 5px; font-weight: bold;">Subject Txt:</label>
    <input type="text" id="sub_" name="sub_" required style="width: 60%; padding: 8px; margin-bottom: 12px; border: 1px solid #ccc; border-radius: 3px;">
    <br>
    <label for="text_" style="display: block; margin-bottom: 5px; font-weight: bold;">Message:</label>
    <textarea id="text_" name="text_" required style="width: 60%; padding: 8px; margin-bottom: 12px; border: 1px solid #ccc; border-radius: 3px; resize: vertical; height: 150px;"></textarea>
    <br>
    <input type="submit" value="Submit" style="background-color: #194D63; color: #fff; padding:10px; border: none; border-radius: 4px;">
  </form>
  `);
});

app.listen(8080, () => {
});
Embedding image using imageUrl.

Code explanation

  • Lines 5–6: Import axios and cors to make a secure request across the domain on HTTP.

  • Line 33: Save the image URL on the imageUrl instance that is to be sent via email.

  • Line 34–35: Use a get request to fetch the response from the imageUrl instance and save it to imageBuffer as a binary file.

  • Line 36: Create an object mailOptions that contains the necessary email configurations.

  • Line 30-40: The object mailOptions consists of the necessary email configurations and sets the request body attributes on the corresponding attribute, including:

    • Recipient's email

    • The subject of the email

    • The text body of the email.

  • Line 41: Write the HTML content that appears in the email body inside the temporal literals.

  • Line 42-45: Create an attachments object and initialize the following instances in it:

    • filename which is the exact name of the local file we want to embed.

    • path which saves the exact path of the file that is saved locally. We used _dirname to fetch the exact path.

    • cid which is a unique instance containing the name of the file with which the file is referred in the html code.

Note: Read the explanation of the remaining code in the text-based email using nodemailer example.

Code output

The following output will be obtained once the code executes successfully. We can see an embedded image with a customized text body and an option to download.

Summary

Emails with embedded images are widely used to make the content more intriguing and attract a larger audience in case of promotions. Nodemailer is an easy-to-use module that can help developers to create customized image-embedded emails and circulate them to a larger audience in a more automated way.

Copyright ©2024 Educative, Inc. All rights reserved