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
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.
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.
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, () => { });
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.
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.
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, () => { });
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.
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.
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.