Sending Email Using Node.js

Lukas White
Share

Most web applications will need to send out the odd email. Whether transactional in nature, or marketing-oriented – for example newsletters, promotions or product recommendations – it’s a pretty ubiquitous requirement. And if you’re sending out email, chances are you’ll want to send them out using HTML.

Actually sending emails is pretty straightforward. That said, there are all sorts of additional issues you need to consider when sending at volume such as handling bounces, tracking clicks, and battling with spam filters.

What we’re more interested in for the purposes of this article, though, is how to handle your HTML email templates, along with all the issues that brings.

Unfortunately, a variety of limitations, quirks and differences between email clients mean that HTML emails are fraught with difficulties.

There are, however, a number of good resources on building HTML emails, even just across SitePoint. There’s Massimo’s guide to building your first email newsletter, Tim’s guide to coding them and Lauren’s series on best practices.

The focus of this article, though, isn’t so much about the problems you face when developing HTML emails, but on some of the tools that can help automate the process for you. We’ll focus on two main areas: sending HTML emails from a Node.js application, and using tools such as Grunt.

Let’s look at a couple of the main issues, and some solutions.

Plain Text Versions

Whilst email clients’ HTML rendering capabilities tend to fall well short of the sort of thing you can do in the browser, most do support HTML – rudimentary as they may be. But not all. Furthermore, some people explicitly prefer to receive plain text versions, and will block HTML versions in their client. We need, therefore, to send a plaintext version as well as your all-singing, all-dancing HTML email.

Broadly speaking, you can take one of two approaches – prepare a separate plain text template, or elect to have it auto-generated from your HTML. Whether the latter is effective or not will probably come down to the complexity and format of your HTML version, and the results can be inconsistent. Personally speaking, I prefer to generate a separate plaintext version since it gives me more control over the output, but if you’d prefer to automate it there are a number of options.

There is a Nodemailer (which we’ll look at shortly) plugin to automatically extract the text from an HTML email for you, and it’s one of a number of tasks performed by Premailer – another tool which we’ll look at later.

Inlining CSS

Because of the limitations of many email clients, you should always inline your CSS.

We’re not simply talking about putting your styles in a <style> tag in the <head> of your HTML email, either. Rather, CSS styles need to be applied to each element using the inline style attribute. Consider the following example:

.btn-primary {
  text-decoration: none;
  color: #FFF;
  background-color: #348eda;
  border: solid #348eda;
  border-width: 10px 20px;
  line-height: 2;
  font-weight: bold;
  text-align: center;
  cursor: pointer;
  display: inline-block;
  border-radius: 5px;
  text-transform: capitalize;
}
<tr>
  <td class="content-block">
    We may need to send you critical information about our service and it is important that we have an accurate email address.
  </td>
</tr>
<tr>
  <td class="content-block">
    <a href="{{ url }}" class="btn-primary">Confirm your email address</a>
  </td>
</tr>

Here is the same HTML fragment, with the CSS inlined:

<tr style="margin: 0; padding: 0; font-family: Helvetica Neue, Helvetica, Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
  <td class="content-block" style="margin: 0; padding: 0 0 20px; font-family: Helvetica Neue, Helvetica, Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top;">
    We may need to send you critical information about our service and it is important that we have an accurate email address.
  </td>
</tr>
<tr style="margin: 0; padding: 0; font-family: Helvetica Neue, Helvetica, Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
  <td class="content-block" style="margin: 0; padding: 0 0 20px; font-family: Helvetica Neue, Helvetica, Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top;">
    <a href="{{ url }}" class="btn-primary" style="margin: 0; padding: 0; font-family: Helvetica Neue, Helvetica, Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; background-color: #348eda; border: solid #348eda; border-width: 10px 20px; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize;">Confirm your email address</a>
  </td>
</tr>

Doing this by hand would, frankly, be a huge chore – not to mention making your HTML email templates almost unmaintainable. It’s also the sort of task which is crying out for automation. Sure enough, there are a number of solutions which we’ll look at now.

Juice

Juice is a JavaScript library for automatically inlining your CSS, making it ideal for HTML emails. Simply provide it with some HTML and a stylesheet, and it will transform it for you into an unmaintainable mess like the example above.

You can use Juice in Node.js applications using the module, with Grunt, or with Gulp.

Grunt Inline CSS

You could also use this Grunt plugin. Usage is simple.

Install the plugin:

npm install grunt-inline-css --save-dev

Register the task:

grunt.loadNpmTasks('grunt-inline-css');

Finally, configure it to tell it what to process:

grunt.initConfig({
  inlinecss: {
    main: {
      options: {
      },
      files: {
        'templates/emails/_inlined/confirm-email.html': 'templates/emails/_raw/confirm-email.html',
        'templates/emails/_inlined/password-reset.html': 'templates/emails/_raw/password-reset.html'
      }
    }
  }
})

It’s worth pointing out that behind the scenes, the plugin is using Juice.

Premailer

Premailer is an online service for processing HTML emails, which performs a number of tasks:

  • Inlines your CSS
  • Converts relative paths, such as links, into absolute paths
  • Checks CSS properties against email client’s capabilities
  • Optionally, it can automatically create an alternative text version for you

Using a web-based service for this is all very well, but having to manually copy-and-paste your source every time you make changes could get tedious pretty quickly. Luckily, there’s also an API, and better still, a package which makes it even easier to use the API from your Node application.

To install the package, run the following:

npm install premailer-api

Here is a simple example of a command-line tool which takes a raw template, stored as a file named in.html, which sends it to Premailer for processing. It then outputs the processed HTML to out.html, and the plain text version to out.txt:

var premailer = require('premailer-api')
  , fs = require('fs');

var template = fs.readFileSync('./in.html', 'utf8');

premailer.prepare(
  {
    html: template 
  }, 
  function(err, email) {  
    fs.writeFileSync('out.html', email.html);
    fs.writeFileSync('out.txt', email.text);
  }
);

To illustrate – given the following HTML:

<!-- in.html -->
<html>
  <head>
    <title>My Email</title>
    <style type="text/css">
      a { color: #336699; }
    </style>
  </head>
  <body>
    Styles inlined with 
    <a href="http://premailer.dialect.ca">Premailer</a> via 
    <a href="https://github.com/JedWatson/node-premailer">node-premailer</a>.
  </body>
<html>

This will give you the following:

<!-- out.html -->
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "https://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
  <title>My Email</title>
  <style type="text/css">
    a { color: #336699; }
  </style>
</head>
<body>
  Styles inlined with 
  <a href="http://premailer.dialect.ca" style="color: #336699;">Premailer</a> via 
  <a href="https://github.com/JedWatson/node-premailer" style="color: #336699;">node-premailer</a>.
</body>
</html>

And the plain text version:

// out.txt
Styles inlined with
Premailer ( http://premailer.dialect.ca ) via
node-premailer ( https://github.com/JedWatson/node-premailer ).

Notice how in addition to inlining the CSS, Premailer has also converted the links to suit plain text.

There are a number of options you can use – consult the Premailer documentation for more details.

There are a number of ways you can use Premailer. There’s a Grunt plugin, a Gulp plugin, or you could roll your own command-line tool along the lines of the example code above.

Other Assets

Chances are that if you’re using HTML emails, they include at least one image. Making your whole email entirely image-based – while frustratingly common – is a huge no-no, but a few images will go a long way to sprucing them up.

There are a number of things you can do with your images. Obviously linked images need to be externally available, so it’s essential you ensure you’re using absolute – not relative – paths. Premailer is one option here.

A common approach is to upload images for an HTML email to a Content Delivery Network (CDN), or a service such as Amazon’s S3.

The Grunt Email Design Workflow package integrates with Rackspace’s Cloud Files, or there are a number of plugins for integrating with S3.

Task Runners

We’ve touched on a few tools which can be used from task runners such as Grunt or Gulp.

An example Grunt/Gulp workflow might involve:

  • Inlining CSS using Juice
  • Identifying images, uploading to S3 and correcting their paths
  • Minifying the HTML
  • Pre-compiling Handlebars templates
  • Combining pre-compiled templates into a JST file

Perhaps the easiest way to use Grunt to process your email templates is to start with the Grunt Email Boilerplate. There’s a lot of functionality there – not all of which you’ll necessarily want to use – so head over to the documentation to see what it can do. And, naturally, there’s something similar for Gulp.

If Yeoman is your thing, there are a number of generators available specifically for HTML emails, such as this one.

Email Templates Library

As far as Node.js solutions go, you might call the Email Templates library the Swiss-Army knife of HTML emails.

It takes care of pretty much the whole process of creating HTML emails, and more.

The library provides the following features:

  • Support for ejs, Jade, Swig, Handlebars, Emblem and Dust
  • CSS pre-processing using Less, SASS, Stylus or Styl
  • CSS inlinig using Juice
  • Integration with Nodemailer, Postmark
  • Support for batch-sending

In other words, it’s the kitchen sink of HTML emails for Node. Let’s take a look at it, and how to use it.

Usage

Install via npm:

npm install email-templates

Next, a create a directory somewhere to hold your templates. Within that, create a directory for each type of email. For example one directory for confirmation emails, another for password reset instructions, and perhaps a generic message wrapper.

Here’s an example directory structure:

templates
  emails
    confirm-email
    password-reset
    welcome

Next, create your templates. At a minimum, each of your email derivatives needs a template for rendering an HTML version. Its filename is important – it must be called html.ext, where .ext represents your templating library of choice. So if you’re using Handlebars, for example, you’ll want to name your file html.hbs, for Jade, html.jade, and so on. Note that you’ll need to ensure that the appropriate templating engine is installed!

Chances are, you’ll also want to create a stylesheet. Call this styles.ext, again using the extension to tell the library how to handle it. .css for standard CSS, or .scss or .less, and so on if you want to use a preprocesser.

If you’d prefer to create your own plain text version, create a file named text.ext. The same rules apply with the extension as with the HTML template, so it will be named something like text.hbs, text.jade, etc.

To illustrate, here’s how you might lay out the confirm-email directory if your templating language of choice is Handlebars, and you happen to prefer SCSS:

templates
  emails
    confirm-email
      html.hbs
      text.hbs
      styles.scss

Next, you’ll need to configure your transport mechanism. The library works seamlessly with NodeMailer and Postmark; for the purposes of this article we’ll use Nodemailer, since it doesn’t require any third-party services, and it’s among the most popular choices for sending email from Node.ks.

In most cases, you’re probably going to want to use Nodemailer over SMTP, although there are all sorts of other transports, from Sendmail to SES and Sendgrid.

Here’s an example using SMTP:

var nodemailer = require('nodemailer');
var transport = nodemailer.createTransport(smtpTransport({
  host: 'smtp.yourprovider.org',
  port: 25,
  auth: {
    user: 'username',
    pass: 'password'
  }
}));

If you’re using Gmail, which uses some non-standard settings, you can use the following shortcut:

var nodemailer = require('nodemailer');
var transport = nodemailer.createTransport({
  service: 'gmail',
  auth: {
    user: 'username@gmail.com',
    pass: 'password'
  }
});

Now that it’s configured, you can send a single email as follows:

emailTemplates(templatesDir, function(err, template) {

  if (err) {
    console.log(err);
  } else {

  var locals = {
    email: 'user@example.com',
    url: 'http://acme.com/confirm/xxx-yyy-zzz'
  };

    // Send a single email
    template('confirm-email', locals, function(err, html, text) {
      if (err) {
        console.log(err);
      } else {
        transport.sendMail({
          from: 'Acme Corp <no-reply@acme.com>',
          to: locals.email,
          subject: 'Please confirm your e-mail address',
          html: html,
          text: text
        }, function(err, responseStatus) {
          if (err) {
            console.log(err);
          } else {
            console.log(responseStatus.message);
          }
        });
      }
    });
  }
});

Pro’s and Con’s of the Email Templates Library

Clearly, the library solves a lot of the problems around HTML emails. It also gives you a huge degree of flexibility. If you want to use Jade and SASS, you can – likewise Handlebars and Less, or Swig and Stylus.

One thing, though – if you have a large volume of emails to send, you can send them in batches and the library will only process your templates once per batch. However, the next time you send an email or a batch of emails, it will run through the whole process again. If you send a LOT of email, you may be better off creating pre-compiled templates.

Bear in mind also that you’ll need to take care of ensuring links and paths to assets are absolute, as well as optionally optimizing your images or uploading assets to a Content Delivery Network (CDN).

Testing Your HTML Emails

Of course, no guide to sending HTML emails would be complete without a note on testing.

Mailcatcher is a really useful tool for “intercepting” emails sent from your application. You simply run it as a background process on your machine, configure your application to use the appropriate port, and you can view the emails via a web-based interface on your machine. Matthew Setter wrote a comprehensive introduction to it here on SitePoint.

It’s also worth checking out Litmus for testing your HTML emails, by comparing screenshots of how your email renders in a variety of clients.

Conclusion

HTML emails can be a real pain, but a lot of the headaches can be mitigated using automation. In this article I’ve looked at a few options, both from Node.js applications and using a task runner such as Grunt. Hopefully I’ve given you enough resources to get you started. Have a look around and see what suits you, your workflow, and your application best. If you know of any other useful tools I haven’t covered, let me know in the comments.