emailjs vs mailgun-js vs nodemailer vs sendgrid
Sending Emails in Node.js Applications
emailjsmailgun-jsnodemailersendgridSimilar Packages:

Sending Emails in Node.js Applications

nodemailer, sendgrid (@sendgrid/mail), mailgun-js, and emailjs are all tools used to send emails from JavaScript applications, but they operate in different ways. nodemailer is a module for Node.js applications to send emails via SMTP, supporting various transports and OAuth2. sendgrid and mailgun-js are official SDKs for specific email service providers (ESPs) that use HTTP APIs instead of SMTP. emailjs refers to an SMTP client for Node.js (distinct from the browser-focused emailjs-com), offering a lighter alternative to nodemailer. Choosing between them depends on whether you need a generic SMTP solution or a tightly integrated API for a specific email provider.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
emailjs02,209319 kB03 months agoMIT
mailgun-js0892-527 years agoMIT
nodemailer017,488541 kB0a month agoMIT-0
sendgrid03,053-889 years agoMIT

Sending Emails in Node.js: Nodemailer vs SendGrid vs Mailgun vs Emailjs

When building backend services in Node.js, sending emails is a common requirement for things like password resets, notifications, and newsletters. You generally have two paths: use a generic SMTP library like nodemailer or emailjs, or use a specific provider's API SDK like sendgrid or mailgun-js. Each approach has trade-offs in setup, flexibility, and vendor lock-in. Let's break down how they handle the core tasks of sending email.

๐Ÿ”Œ Connection Model: SMTP vs HTTP API

The biggest difference lies in how these libraries talk to the mail server.

nodemailer uses SMTP (Simple Mail Transfer Protocol).

  • You need a host, port, and credentials (username/password or OAuth2).
  • Works with almost any email provider (Gmail, AWS SES, custom servers).
// nodemailer: SMTP Transport
const transporter = nodemailer.createTransport({
  host: 'smtp.example.com',
  port: 587,
  secure: false,
  auth: { user: 'user', pass: 'pass' }
});

await transporter.sendMail({ from, to, subject, text });

emailjs also uses SMTP but aims for a lighter footprint.

  • Similar setup to nodemailer but with a different API structure.
  • Good for simple SMTP connections without extra plugins.
// emailjs: SMTP Client
const server = email.server.connect({
  host: 'smtp.example.com',
  ssl: true,
  user: 'user',
  password: 'pass'
});

server.send({ from, to, subject, text }, callback);

sendgrid uses Twilio SendGrid's HTTP API.

  • No SMTP ports to manage; just an API key.
  • Requires a SendGrid account; you cannot swap providers easily.
// sendgrid (@sendgrid/mail): HTTP API
const msg = {
  to: 'recipient@example.com',
  from: 'sender@example.com',
  subject: 'Hello',
  text: 'World',
};

await sgMail.send(msg);

mailgun-js uses Mailgun's HTTP API.

  • Connects via API key and domain name.
  • Like SendGrid, you are locked into the Mailgun ecosystem.
// mailgun-js: HTTP API
const formData = {
  from: 'You <mailgun@yourdomain.com>',
  to: 'recipient@example.com',
  subject: 'Hello',
  text: 'World'
};

mailgun.messages().send(formData, callback);

๐Ÿ“ Message Construction: Objects vs Builders

How you build the email content varies slightly between libraries.

nodemailer uses a straightforward object for message data.

  • Supports HTML, text, attachments, and CC/BCC in one object.
  • Very explicit and easy to read.
// nodemailer: Message Object
const mailOptions = {
  from: 'sender@example.com',
  to: 'recipient@example.com',
  subject: 'Test',
  html: '<b>Hello</b>',
  attachments: [{ path: 'file.pdf' }]
};

emailjs uses a similar object but with slightly different keys.

  • Text and HTML are often combined or handled distinctly.
  • Attachments require specific formatting.
// emailjs: Message Object
const message = {
  from: 'sender@example.com',
  to: 'recipient@example.com',
  subject: 'Test',
  text: 'Hello',
  attachment: [{ data: fs.readFileSync('file.pdf') }]
};

sendgrid uses a structured object tailored to SendGrid features.

  • Supports dynamic templates and personalizations.
  • Attachment data must be base64 encoded.
// sendgrid: Message Object
const msg = {
  to: 'recipient@example.com',
  from: 'sender@example.com',
  templateId: 'd-your-template-id',
  dynamic_template_data: { name: 'User' },
  attachments: [{ content: base64Data, filename: 'file.pdf' }]
};

mailgun-js uses form-data style properties.

  • Often passes data as a simple hash or form object.
  • Template variables are prefixed with o: or passed in h: headers.
// mailgun-js: Message Data
const data = {
  from: 'sender@yourdomain.com',
  to: 'recipient@example.com',
  subject: 'Test',
  text: 'Hello',
  inline: fs.readFileSync('image.png')
};

โš™๏ธ Configuration and Setup

Setting up these libraries ranges from installing a package to configuring DNS records.

nodemailer requires zero external dependencies beyond the package.

  • You just need valid SMTP credentials.
  • Great for local development with tools like Mailtrap.
// nodemailer: Install and Init
// npm install nodemailer
import nodemailer from 'nodemailer';
// Configure transport as shown above

emailjs is also a simple npm install.

  • No external service required if you have an SMTP server.
  • Less documentation available compared to nodemailer.
// emailjs: Install and Init
// npm install emailjs
import email from 'emailjs';
// Connect to server as shown above

sendgrid requires an API Key from the SendGrid dashboard.

  • You must verify your sender identity to avoid spam filters.
  • Setup is fast but ties you to their platform.
// sendgrid: Install and Init
// npm install @sendgrid/mail
import sgMail from '@sendgrid/mail';
sgMail.setApiKey(process.env.SENDGRID_API_KEY);

mailgun-js requires an API Key and a verified Domain.

  • You need to configure DNS records (MX, TXT) for your domain.
  • More setup work initially but powerful for high volume.
// mailgun-js: Install and Init
// npm install mailgun-js
const mailgun = require('mailgun-js')({ apiKey, domain });

โš ๏ธ Maintenance and Future Proofing

This is a critical factor for long-term projects.

nodemailer is actively maintained and widely adopted.

  • It is the de facto standard for Node.js email.
  • Safe to use for new and existing projects.

emailjs has slower update cycles and a smaller community.

  • It works, but you might find fewer answers to problems online.
  • Consider nodemailer if you want long-term security.

sendgrid is actively maintained by Twilio.

  • The @sendgrid/mail package is the current standard.
  • Regular updates for security and new features.

mailgun-js is considered legacy; the new package is mailgun.js.

  • Using mailgun-js in new projects is not recommended.
  • If you choose Mailgun, use the updated mailgun.js client instead.

๐ŸŒ Real-World Scenarios

Scenario 1: Startup MVP with Gmail

You need to send welcome emails quickly without paying for a service.

  • โœ… Best choice: nodemailer
  • Why? You can use Gmail's SMTP for free (with limits) and switch later easily.
// nodemailer with Gmail
const transporter = nodemailer.createTransport({
  service: 'gmail',
  auth: { user, pass }
});

Scenario 2: High Volume Transactional Emails

You need to send password resets for thousands of users daily with tracking.

  • โœ… Best choice: sendgrid
  • Why? Dedicated infrastructure ensures delivery, and API gives open/click tracking.
// sendgrid with tracking
const msg = { ... , trackingSettings: { clickTracking: { enable: true } } };

Scenario 3: Legacy System Maintenance

You are fixing bugs in an older app that already uses Mailgun.

  • โœ… Best choice: mailgun-js (or migrate to mailgun.js)
  • Why? Changing email providers is risky; stick to what works or plan a migration.
// mailgun-js existing code
mailgun.messages().send(data, ...);

Scenario 4: Lightweight Microservice

You have a small containerized service that just needs to alert admins.

  • โœ… Best choice: emailjs
  • Why? If you already have an SMTP relay, this adds minimal overhead.
// emailjs simple alert
server.send(message, callback);

๐Ÿ“Œ Summary Table

Featurenodemaileremailjssendgridmailgun-js
ProtocolSMTPSMTPHTTP APIHTTP API
Vendor Lock-inNone (Generic)None (Generic)High (Twilio)High (Mailgun)
Maintenanceโœ… Activeโš ๏ธ Slowโœ… Activeโš ๏ธ Legacy (Use mailgun.js)
Setup ComplexityLowLowMedium (API Key)High (DNS + API)
Best ForGeneral PurposeSimple SMTPTransactionalMarketing/Devops

๐Ÿ’ก Final Recommendation

For most Node.js developers, nodemailer is the safest and most flexible choice. It lets you change email providers without rewriting your code, which is a huge benefit as your project grows. It is the standard for a reason โ€” it works everywhere.

If you need advanced features like detailed analytics, template management, or guaranteed deliverability at scale, pick a provider-specific SDK like sendgrid. Just remember that you are locking yourself into their platform.

Avoid mailgun-js for new work; use mailgun.js if you choose Mailgun. Use emailjs only if you have a specific reason to prefer it over nodemailer, as the community support is much smaller.

Final Thought: Email is critical infrastructure. Don't over-optimize for bundle size here โ€” optimize for deliverability and maintainability. Pick the tool that ensures your emails actually reach the inbox.

How to Choose: emailjs vs mailgun-js vs nodemailer vs sendgrid

  • emailjs:

    Choose emailjs if you need a lightweight SMTP client for Node.js and prefer a simpler API than nodemailer for basic use cases. It is suitable for small projects where you manage your own SMTP server or use a standard provider without needing advanced plugins. However, be aware that it has a smaller community and fewer features compared to nodemailer, so verify its maintenance status before committing to large-scale production use.

  • mailgun-js:

    Choose mailgun-js only if you are maintaining a legacy project that already depends on it, as the official library has moved to mailgun.js. For new projects, avoid this package in favor of the updated mailgun.js SDK or other alternatives. It connects to the Mailgun HTTP API, which is useful if you need advanced tracking and templating features specific to Mailgun, but ensure you are using the actively maintained version.

  • nodemailer:

    Choose nodemailer if you need a flexible, provider-agnostic solution that works with any SMTP server, including Gmail, AWS SES, or your own mail server. It is the industry standard for Node.js email sending, offering a rich plugin ecosystem for templates, OAuth2, and custom transports. This is the best choice for applications that might switch email providers later or need full control over the SMTP connection without being locked into a specific vendor's API.

  • sendgrid:

    Choose sendgrid (@sendgrid/mail) if you are using Twilio SendGrid as your email provider and want official support for their HTTP API features. It simplifies sending transactional emails with built-in support for templates, tracking, and attachments without managing SMTP credentials. This is ideal for teams already invested in the SendGrid ecosystem who need reliable delivery and detailed analytics provided directly through the API.

README for emailjs

emailjs ๐Ÿ“งโœจ

Send emails with ease!

This library lets you send rich HTML emails, attachments (from files, streams, or strings), and plain text messages to any SMTP server.

checks

What's to expect from emailjs? ๐Ÿš€

  • SSL and TLS Support: Secure connections to your SMTP servers.
  • Authentication Galore: Supports popular SMTP authentication methods like PLAIN, LOGIN, CRAM-MD5, and XOAUTH2.
  • Asynchronous Sending: Emails are queued and sent in the background.
  • Rich Content: Send HTML emails and include multiple attachments.
  • Flexible Attachments: Attachments can be files, data streams, or plain strings.
  • UTF-8 Ready: Full support for UTF-8 in headers and body.
  • Built-in Type Declarations: first-class TypeScript support.
  • Greylisting Awareness: Automatically handles greylisting to improve deliverability.

Get Started! ๐Ÿ› ๏ธ

Installing

It's super simple!

npm install emailjs

Requirements

  • Access to an SMTP Server.
  • If your email service (like Gmail) uses two-step verification, you'll need an application-specific password.

Quick Examples ๐Ÿง‘โ€๐Ÿ’ป

Here's how easy it is to send emails:

Text-Only Emails

import { SMTPClient } from 'emailjs';

const client = new SMTPClient({
    user: 'your-username',
    password: 'your-password',
    host: 'smtp.your-email.com',
    ssl: true, // Use SSL for secure connection
});

async function sendMyEmail() {
    try {
        const message = await client.sendAsync({
            text: 'Hello from emailjs! This is a test message.',
            from: 'You <your-email@example.com>',
            to: 'Someone <someone@example.com>',
            subject: 'Exciting News from emailjs! ๐ŸŽ‰',
        });
        console.log('Email sent successfully:', message);
    } catch (err) {
        console.error('Failed to send email:', err);
    } finally {
        client.smtp.close(); // Don't forget to close the connection!
    }
}

sendMyEmail();

HTML Emails & Attachments

import { SMTPClient, Message } from 'emailjs';

const client = new SMTPClient({
    user: 'your-username',
    password: 'your-password',
    host: 'smtp.your-email.com',
    tls: true,
});

async function sendRichEmail() {
    const htmlContent = `
        <h1>Greetings!</h1>
        <p>This is an <b>HTML email</b> with a lovely picture and an attachment.</p>
        <img src="cid:my-image" alt="Embedded Image" width="150" height="100">
        <p>Check out the attached file!</p>
    `;

    const message = new Message({
        from: 'You <your-email@example.com>',
        to: 'Someone <someone@example.com>',
        subject: 'Your Awesome HTML Email! ๐Ÿ–ผ๏ธ๐Ÿ“„',
        attachment: [
            {
                data: htmlContent,
                alternative: true, // This part is the HTML body
                contentType: 'text/html',
            },
            {
                path: 'path/to/your/document.pdf', // Attach a file from disk
                type: 'application/pdf',
                name: 'document.pdf',
            },
            {
                path: 'path/to/your/image.jpg', // Embed an image for the HTML
                type: 'image/jpeg',
                name: 'cool_image.jpg',
                // Reference in HTML with cid:my-image
                headers: { 'Content-ID': '<my-image>' },
            },
        ],
    });

    try {
        await client.sendAsync(message);
        console.log('Rich email sent successfully!');
    } catch (err) {
        console.error('Failed to send rich email:', err);
    } finally {
        client.smtp.close();
    }
}

sendRichEmail();

API Reference ๐Ÿ“–

The emailjs library is fully typed, here is a brief overview of most likely to be used methods

new SMTPClient(options)

Create a new client instance to connect to your SMTP server.

const options = {
    user: 'your-username', // ๐Ÿ”‘ Username for logging into SMTP
    password: 'your-password', // ๐Ÿคซ Password for logging into SMTP
    host: 'smtp.your-email.com', // ๐ŸŒ SMTP server host (defaults to 'localhost')
    port: 587, // ๐Ÿ”Œ SMTP port (defaults: 25 unencrypted, 465 SSL, 587 TLS)
    ssl: true, // ๐Ÿ”’ Boolean or object for immediate SSL connection
    tls: true, // ๐Ÿ” Boolean or object (see typescript types) to initiate STARTTLS
    timeout: 5000, // โณ Max milliseconds to wait for SMTP responses
    domain: 'your-domain.com', // ๐Ÿ  Domain to greet SMTP with (defaults to os.hostname)
    authentication: ['PLAIN', 'LOGIN'], // ๐Ÿค Preferred authentication methods
    logger: console, // ๐Ÿ“ Override the built-in logger (e.g., custom logging)
};

SMTPClient#send(message, callback)

Sends an email message. You can pass a Message instance or a headers object.

client.send(messageObject, (err, details) => {
    if (err) console.error(err);
    else console.log('Message sent:', details);
});

SMTPClient#sendAsync(message)

a promise-based way to send emails! โœจ

try {
    const details = await client.sendAsync(messageObject);
    console.log('Message sent:', details);
} catch (err) {
    console.error('Failed to send:', err);
}

new Message(headers)

Constructs an RFC2822-compliant message object.

const headers = {
    from: 'sender@example.com', // ๐Ÿ’Œ Sender (required!)
    to: 'recipient@example.com', // ๐Ÿ“ฌ Recipients (at least one of to, cc, or bcc)
    cc: 'carbon-copy@example.com', // ๐Ÿ‘ฅ CC recipients
    bcc: 'blind-copy@example.com', // ๐Ÿ•ต๏ธโ€โ™€๏ธ BCC recipients
    subject: 'Your Subject Here', // ๐Ÿ“ Email subject
    text: 'Plain text body.', // ๐Ÿ—’๏ธ Plain text content
    attachment: [{ data: 'Hello!' }], // ๐Ÿ“Ž One or more attachments
};

Message#attach(options)

Adds an attachment to the message. Can be called multiple times.

message.attach({
    path: 'path/to/file.zip', // ๐Ÿ“ Path to a file on disk
    data: 'Binary content as string or buffer', // ๐Ÿ“„ Raw data
    stream: fs.createReadStream('file.jpg'), // ๐ŸŒŠ A readable stream
    type: 'application/zip', // MIME type
    name: 'custom-name.zip', // Filename perceived by recipient
    alternative: true, // attach inline as an alternative (e.g., HTML body)
    inline: true, // If true, attached inline (e.g., for <img src="cid:...">)
    headers: { 'X-Custom-Header': 'value' }, // Custom attachment headers
});

Message#checkValidity()

Synchronously validates that a Message is properly formed before sending.

const { isValid, validationError } = message.checkValidity();
if (!isValid) {
    console.error('Message is invalid:', validationError);
}

Authors โœ๏ธ

  • eleith
  • zackschuster

Testing ๐Ÿงช

# Run all tests
npm test

# Run tests with code coverage report
npm run test:coverage

Development ๐Ÿง‘โ€๐Ÿ’ป๐ŸŒฑ

for a local smtp testing experience, use our Mailpit compose service

1. Start Mailpit with Docker Compose

Ensure you have Docker and Docker Compose installed.

# From the project root, start Mailpit
docker compose up

Mailpit will be accessible via:

  • Web UI: http://localhost:8025
  • SMTP Server: localhost:1025

2. Run Example Sending Scripts

You can use the provided scripts to send different types of emails to your local Mailpit instance.

First, make sure the emailjs library is built:

npm run build

Then, run any of the example scripts:

# Send a plain text email
node scripts/send-text.js

# Send an HTML email
node scripts/send-html.js

# Send an email with attachments
node scripts/send-attachment.js

After running a script, open your Mailpit Web UI (http://localhost:8025) to see the emails stream in! ๐Ÿ“ฉ