How to Send Emails in Node.js Using Nodemailer (Ultimate Guide)

5 minute read

Sending emails is a fundamental task in many web applications — from user registration confirmations to password resets, invoices, and marketing campaigns. If you’re building a Node.js app and need to send emails, Nodemailer is hands down the most popular and reliable library.

In this detailed guide, you’ll learn:

  • What Nodemailer is and why it’s used
  • How to install and configure Nodemailer
  • How to send a simple email
  • How to add attachments
  • How to use HTML email templates
  • How to integrate with services like Gmail, Outlook, SendGrid
  • Common errors and how to fix them
  • Best practices for production

By the end, you’ll have a complete understanding and a practical working setup to send emails in your Node.js projects confidently.

Let’s dive right in! 🚀


What is Nodemailer?

Nodemailer is a Node.js module that enables you to easily send emails. It’s a zero-dependency package — simple to set up and flexible enough to handle everything from basic messages to complex email services.

Key Features of Nodemailer:

  • SMTP and other transport protocols supported
  • Attachments support
  • HTML content, embedded images
  • OAuth2 authentication (for Gmail, Outlook, etc.)
  • Unicode support
  • Ability to send bulk or scheduled emails

It’s like having your own tiny mail server inside your app without needing to overcomplicate things.


Setting Up Your Node.js Project

Before we start sending emails, let’s set up our project.

Step 1: Initialize a Node.js Project

mkdir nodemailer-tutorial
cd nodemailer-tutorial
npm init -y

This creates a package.json file.

Step 2: Install Nodemailer

Now, install Nodemailer:

npm install nodemailer

You’re all set to begin coding!


Sending Your First Email

Now let’s get your first email sent.

Step 1: Create a File

Create a file called sendEmail.js.

Step 2: Basic Code to Send an Email

const nodemailer = require('nodemailer');

// Create a transporter
let transporter = nodemailer.createTransport({
    service: 'gmail',
    auth: {
        user: 'your-email@gmail.com',
        pass: 'your-email-password'
    }
});

// Email options
let mailOptions = {
    from: 'your-email@gmail.com',
    to: 'recipient@example.com',
    subject: 'Hello from Node.js',
    text: 'This is a test email sent from Node.js using Nodemailer.'
};

// Send email
transporter.sendMail(mailOptions, (error, info) => {
    if (error) {
        console.log('Error occurred: ', error.message);
    } else {
        console.log('Email sent: ', info.response);
    }
});

Run it with:

node sendEmail.js

And if everything is set correctly, your email will be sent!


Important Notes on Gmail

If you’re using Gmail:

  • Less secure apps access must be enabled on your account.
  • Alternatively, you should use OAuth2 authentication for better security (explained later).
  • Google may block sign-in attempts; you might need to create an App Password.

Understanding Nodemailer Components

Let’s break down what’s happening:

Component Meaning
Transporter A configuration that tells Nodemailer how to send emails (e.g., via Gmail SMTP).
Mail Options The email content: from, to, subject, text, HTML, attachments, etc.
sendMail() The method that actually sends the email.

Once you understand these three, you can build any email functionality!


Sending HTML Emails

Text emails are boring! Let’s add some HTML content.

let mailOptions = {
    from: 'your-email@gmail.com',
    to: 'recipient@example.com',
    subject: 'HTML Email Example',
    html: `<h1>Hello 👋</h1><p>This is an <b>HTML email</b> sent using Nodemailer!</p>`
};

HTML allows you to style your emails however you want.

You can even add inline CSS for beautiful designs.


Adding Attachments

Need to send PDFs, images, or files?

Here’s how:

let mailOptions = {
    from: 'your-email@gmail.com',
    to: 'recipient@example.com',
    subject: 'Sending Attachments',
    text: 'Please find attached files.',
    attachments: [
        {
            filename: 'document.txt',
            path: './document.txt'
        },
        {
            filename: 'image.png',
            path: './image.png'
        }
    ]
};

Nodemailer will handle the file encoding automatically. Easy!


Embedding Images Inside Emails (Inline Images)

Sometimes you want to show images inside the email body, not just as attachments.

Example:

let mailOptions = {
    from: 'your-email@gmail.com',
    to: 'recipient@example.com',
    subject: 'Email with Embedded Image',
    html: `<h1>Look at this image:</h1><img src="cid:unique@nodemailer.com"/>`,
    attachments: [{
        filename: 'image.png',
        path: './image.png',
        cid: 'unique@nodemailer.com' // same CID as in HTML
    }]
};

This displays the image inside the email rather than as a download.


Using Email Templates

Hardcoding HTML is messy. Instead, use templating engines like Handlebars, EJS, or Pug.

Here’s a quick example using Handlebars:

Install dependencies:

npm install nodemailer-express-handlebars

Setup with Handlebars:

const hbs = require('nodemailer-express-handlebars');
const path = require('path');

transporter.use('compile', hbs({
    viewEngine: {
        extName: '.hbs',
        partialsDir: path.resolve('./templates'),
        defaultLayout: false,
    },
    viewPath: path.resolve('./templates'),
    extName: '.hbs'
}));

let mailOptions = {
    from: 'your-email@gmail.com',
    to: 'recipient@example.com',
    subject: 'Welcome Email',
    template: 'welcome', // name of your file 'welcome.hbs'
    context: {
        name: 'John Doe',
        company: 'NodeMailer Inc.'
    }
};

In templates/welcome.hbs:

<h1>Hello </h1>
<p>Welcome to !</p>

Dynamic, clean, and easy to maintain.


Instead of sending directly via SMTP, you can use email providers.

Here’s a quick setup:

Gmail

(Already shown above, with service: 'gmail'.)

Outlook

service: 'hotmail',
auth: {
    user: 'your-email@hotmail.com',
    pass: 'your-password'
}

SendGrid

For bulk transactional emails:

host: 'smtp.sendgrid.net',
port: 587,
auth: {
    user: 'apikey',
    pass: 'YOUR_SENDGRID_API_KEY'
}

More reliable than Gmail for production.


Handling Errors

When sending emails, errors can happen:

  • Wrong credentials
  • SMTP server unavailable
  • Invalid recipient email
  • Attachments missing

You should wrap sendMail() in a try-catch block:

async function sendEmail() {
    try {
        let info = await transporter.sendMail(mailOptions);
        console.log('Email sent: ', info.response);
    } catch (error) {
        console.error('Error sending email: ', error.message);
    }
}

Always validate user input too!


Common Errors and Fixes

Error Solution
Invalid login Check your email and password or OAuth2 settings
Connection timeout SMTP server unreachable; check host/port
Blocked by Gmail Enable less secure apps or use App Passwords
Self-signed certificate error Add tls: { rejectUnauthorized: false } in transporter

Example adding TLS settings:

let transporter = nodemailer.createTransport({
    host: 'smtp.example.com',
    port: 587,
    secure: false,
    auth: {
        user: 'your-email@example.com',
        pass: 'your-password'
    },
    tls: {
        rejectUnauthorized: false
    }
});

Advanced: Sending Bulk Emails

Sending thousands of emails? Use loops + delays to avoid hitting limits:

Example:

const recipients = ['a@example.com', 'b@example.com', 'c@example.com'];

recipients.forEach((email, index) => {
    setTimeout(() => {
        transporter.sendMail({
            from: 'your-email@gmail.com',
            to: email,
            subject: 'Personalized Email',
            text: `Hello ${email}, this is a test.`
        }, (err, info) => {
            if (err) {
                console.log(`Error sending to ${email}:`, err.message);
            } else {
                console.log(`Email sent to ${email}:`, info.response);
            }
        });
    }, index * 2000); // 2-second delay between emails
});

This way, you won’t overwhelm the server.


Best Practices for Emailing

  • Use a dedicated email service (SendGrid, Mailgun) in production.
  • Validate all email inputs.
  • Avoid sending sensitive information unencrypted.
  • Always catch and log errors.
  • If sending bulk, add delays or use services built for bulk mailing.
  • Monitor your email sending rates and bounce rates.

Conclusion

Nodemailer is a simple yet powerful tool for sending emails with Node.js.
With just a few lines of code, you can integrate advanced emailing features into your app, from simple notifications to complex templated newsletters.

In this guide, you learned:

  • Setting up Nodemailer
  • Sending text, HTML, attachments
  • Using templates
  • Handling errors
  • Integrating with email services
  • Best practices for production

If you follow the patterns and tips shared here, you’ll be able to build reliable email services for any application.


Bonus Resources



Leave a comment