@ourharvest/email-templates v5.0.5
Email Templates
:heart: Love this project? Support @niftylettuce's FOSS on Patreon or PayPal :unicorn:
Create, preview, and send custom email templates for Node.js. Highly configurable and supports automatic inline CSS, stylesheets, embedded images and fonts, and much more! Made for sending beautiful emails with Lad.
Still on v4.x?: v5.x is released with only one minor breaking change, see breaking changes below.
Still on v2.x?: v3.x is released (you'll need Node v6.4.0+); see breaking changes below. 2.x branch docs available if necessary.
Table of Contents
- Install
- Preview
- Usage
- Debugging
- Basic
- Attachments
- Automatic Inline CSS via Stylesheets
- Render HTML and/or Text
- Cache Pug Templates
- Localization
- Text-Only Email (no HTML)
- Prefix Subject Lines
- Custom Text Template
- Custom Template Engine (e.g. EJS)
- Custom Default Message Options
- Custom Rendering (e.g. from a MongoDB database)
- Absolute Path to Templates
- Open Email Previews in Firefox
- Options
- Plugins
- V5 Breaking Changes
- V3 Breaking Changes
- Tip
- Related
- Contributors
- License
Install
By default we recommend pug for your template engine, but you can use any template engine.
npm:
npm install email-templates pugyarn:
yarn add email-templates pugPreview
We've added preview-email by default to this package!
This means that (by default) in the development environment (e.g. NODE_ENV=development) your emails will be rendered to the tmp directory for you and automatically opened in the browser.
If you have trouble previewing emails in your browser, you can configure a preview option which gets passed along to opn's options (e.g. { app: 'firefox' }). See the example below for Open Email Previews in Firefox.
View the demo
Usage
UPGRADING? If you are upgrading from v2 to v3, see v3 Breaking Changes below. You'll need Node v6.4.0+ now.
Debugging
Environment Flag
If you run into any issues with configuration, files, templates, locals, etc, then you can use the DEBUG environment flag:
DEBUG=email-templates node app.jsThis will output to the console all debug statements in our codebase for this package.
Inspect Message
As of v3.6.1 you can now inspect the message passed to nodemailer.sendMail internally.
In the response object from email.send, you have access to res.originalMessage:
email
.send({
template: 'mars',
message: {
to: 'elon@spacex.com'
},
locals: {
name: 'Elon'
}
})
.then(res => {
console.log('res.originalMessage', res.originalMessage)
});
.catch(console.error);Basic
You can swap the
transportoption with a Nodemailer transport configuration object or transport instance. We highly recommend using Postmark for your transport (it's the default in Lad).If you want to send emails in
developmentortestenvironments, setoptions.sendtotrue.
const Email = require('email-templates');
const email = new Email({
message: {
from: 'niftylettuce@gmail.com'
},
// uncomment below to send emails in development/test env:
// send: true
transport: {
jsonTransport: true
}
});
email
.send({
template: 'mars',
message: {
to: 'elon@spacex.com'
},
locals: {
name: 'Elon'
}
})
.then(console.log)
.catch(console.error);The example above assumes you have the following directory structure:
.
├── app.js
└── emails
└── mars
├── html.pug
└── subject.pugAnd the contents of the pug files are:
html.pug:
p Hi #{name},
p Welcome to Mars, the red planet.
subject.pug:
= `Hi ${name}, welcome to Mars`Attachments
Please reference Nodemailer's attachment documentation for further reference.
If you want to set default attachments sent with every email:
const Email = require('email-templates');
const email = new Email({
message: {
from: 'niftylettuce@gmail.com',
attachments: [
{
filename: 'text1.txt',
content: 'hello world!'
}
]
}
});
email
.send({
template: 'mars',
message: {
to: 'elon@spacex.com'
},
locals: {
name: 'Elon'
}
})
.then(console.log)
.catch(console.error);If you want to set attachments sent individually:
const Email = require('email-templates');
const email = new Email({
message: {
from: 'niftylettuce@gmail.com'
},
transport: {
jsonTransport: true
}
});
email
.send({
template: 'mars',
message: {
to: 'elon@spacex.com',
attachments: [
{
filename: 'text1.txt',
content: 'hello world!'
}
]
},
locals: {
name: 'Elon'
}
})
.then(console.log)
.catch(console.error);Automatic Inline CSS via Stylesheets
Simply include the path or URL to the stylesheet in your template's <head>:
link(rel="stylesheet", href="/css/app.css", data-inline)This will look for the file /css/app.css in the build/ folder.
If this asset is in another folder, then you will need to modify the default options when creating an Email instance:
const email = new Email({
// <https://github.com/Automattic/juice>
juice: true,
juiceResources: {
preserveImportant: true,
webResources: {
//
// this is the relative directory to your CSS/image assets
// and its default path is `build/`:
//
// e.g. if you have the following in the `<head`> of your template:
// `<link rel="stylesheet" href="style.css" data-inline="data-inline">`
// then this assumes that the file `build/style.css` exists
//
relativeTo: path.resolve('build')
//
// but you might want to change it to something like:
// relativeTo: path.join(__dirname, '..', 'assets')
// (so that you can re-use CSS/images that are used in your web-app)
//
}
}
});Render HTML and/or Text
If you don't need this module to send your email, you can still use it to render HTML and/or text templates.
Simply use the email.render(view, locals) method we expose (it's the same method that email.send uses internally).
If you need to render a specific email template file (e.g. the HTML version):
const Email = require('email-templates');
const email = new Email();
email
.render('mars/html', {
name: 'Elon'
})
.then(console.log)
.catch(console.error);The example above assumes you have the following directory structure (note that this example would only render the html.pug file):
.
├── app.js
└── emails
└── mars
├── html.pug
├── text.pug
└── subject.pugThe Promise for email.render resolves with a String (the HTML or text rendered).
If you need to render all available template files for a given email template (e.g.
html.pug,text.pug, andsubject.pug– you can useemail.renderAll(this is the method thatemail.senduses).
const Email = require('email-templates');
const email = new Email();
email
.renderAll('mars', {
name: 'Elon'
})
.then(console.log)
.catch(console.error);If you need to render multiple, specific templates at once (but not all email templates available), then you can use
Promise.allin combination withemail.render:
const Email = require('email-templates');
const email = new Email();
const locals = { name: 'Elon' };
Promise
.all([
email.render('mars/html', locals),
email.render('mars/text', locals)
])
.then(([ html, text ]) => {
console.log('html', html);
console.log('text', text);
})
.catch(console.error);Cache Pug Templates
We strongly suggest to follow this example and pre-cache your templates with cache-pug-templates (if you're using the default Pug template engine).
If you do not do this, then your Pug templates will re-compile and re-cache every time you deploy new code and restart your app.
Ensure you have Redis (v4.x+) installed:
- Mac:
brew install redis && brew services start redis Ubuntu:
sudo add-apt-repository -y ppa:chris-lea/redis-server sudo apt-get update sudo apt-get -y install redis-server
- Mac:
Install the packages:
npm:
npm install cache-pug-templates redisyarn:
yarn add cache-pug-templates redisConfigure it to read and cache your entire email templates directory:
const path = require('path'); const cachePugTemplates = require('cache-pug-templates'); const redis = require('redis'); const Email = require('email-templates'); const redisClient = redis.createClient(); const email = new Email({ message: { from: 'niftylettuce@gmail.com' }, transport: { jsonTransport: true } }); cachePugTemplates(redisClient, email.config.views.root); // ...For more configuration options see cache-pug-templates.
Localization
All you need to do is simply pass an i18n configuration object as config.i18n (or an empty one as this example shows to use defaults).
Don't want to handle localization and translation yourself? Just use Lad – it's built in and uses mandarin (with automatic Google Translate support) under the hood!
const Email = require('email-templates');
const email = new Email({
message: {
from: 'niftylettuce@gmail.com'
},
transport: {
jsonTransport: true
},
i18n: {} // <------ HERE
});
email
.send({
template: 'mars',
message: {
to: 'elon@spacex.com'
},
locals: {
locale: 'en', // <------ CUSTOMIZE LOCALE HERE (defaults to `i18n.defaultLocale` - `en`)
// is your user french?
// locale: 'fr',
name: 'Elon'
}
})
.then(console.log)
.catch(console.error);Then slightly modify your templates to use localization functions.
html.pug:
p= `${t('Hi')} ${name},`
p= t('Welcome to Mars, the red planet.')
subject.pug:
p= `${t('Hi')} ${name}, ${t('welcome to Mars')}`Note that if you use Lad, you have a built-in filter called translate:
p: :translate(locale) Welcome to Mars, the red planet.Text-Only Email (no HTML)
If you wish to have only a text-based version of your email you can simply pass the option textOnly: true.
Regardless if you use the htmlToText option or not (see next example), it will still render only a text-based version.
const Email = require('email-templates');
const email = new Email({
message: {
from: 'niftylettuce@gmail.com'
},
transport: {
jsonTransport: true
},
textOnly: true // <----- HERE
});
email
.send({
template: 'mars',
message: {
to: 'elon@spacex.com'
},
locals: {
name: 'Elon'
}
})
.then(console.log)
.catch(console.error);Prefix Subject Lines
You can pass an option to prefix subject lines with a string, which is super useful for deciphering development / staging / production environment emails.
For example, you could make it so on non-production environments the email is prefixed with a [DEVELOPMENT] Some Subject Line Here.
You could do this manually by passing a message.subject property, however if you are storing your subject lines in templates (e.g. subject.ejs or subject.pug) then it's not as easy.
Simply use the subjectPrefix option and set it to whatever you wish (note you will need to append a trailing space if you wish to have a space after the prefix; see example below):
const Email = require('email-templates');
const env = process.env.NODE_ENV || 'development';
const email = new Email({
message: {
from: 'niftylettuce@gmail.com'
},
transport: {
jsonTransport: true
},
subjectPrefix: env === 'production' ? false : `[${env.toUpperCase()}] `; // <--- HERE
});Custom Text Template
By default we use
html-to-textto generate a plaintext version and attach it asmessage.text.
If you'd like to customize the text body, you can pass message.text or create a text template file just like you normally would for html and subject.
You may also set config.htmlToText: false to force the usage of the text template file.
const Email = require('email-templates');
const email = new Email({
message: {
from: 'niftylettuce@gmail.com'
},
transport: {
jsonTransport: true
},
htmlToText: false // <----- HERE
});
email
.send({
template: 'mars',
message: {
to: 'elon@spacex.com'
},
locals: {
name: 'Elon'
}
})
.then(console.log)
.catch(console.error);
text.pug:
| Hi #{name},
| Welcome to Mars, the red planet.Custom Template Engine (e.g. EJS)
Install your desired template engine (e.g. EJS)
npm:
npm install ejsyarn:
yarn add ejsSet the extension in options and send an email
const Email = require('email-templates'); const email = new Email({ message: { from: 'niftylettuce@gmail.com' }, transport: { jsonTransport: true }, views: { options: { extension: 'ejs' // <---- HERE } } });
Custom Default Message Options
You can configure your Email instance to have default message options, such as a default "From", an unsubscribe header, etc.
For a list of all available message options and fields see the Nodemailer message reference.
Here's an example showing how to set a default custom header and a list unsubscribe header:
const Email = require('email-templates');
const email = new Email({
message: {
from: 'niftylettuce@gmail.com',
headers: {
'X-Some-Custom-Thing': 'Some-Value'
},
list: {
unsubscribe: 'https://niftylettuce.com/unsubscribe'
}
},
transport: {
jsonTransport: true
}
});Custom Rendering (e.g. from a MongoDB database)
You can pass a custom config.render function which accepts two arguments view and locals and must return a Promise.
Note that if you specify a custom config.render, you should have it use email.juiceResources before returning the final HTML. The example below shows how to do this.
If you wanted to read a stored EJS template from MongoDB, you could do something like:
const ejs = require('ejs');
const email = new Email({
// ...
render: (view, locals) => {
return new Promise((resolve, reject) => {
// this example assumes that `template` returned
// is an ejs-based template string
// view = `${template}/html` or `${template}/subject` or `${template}/text`
db.templates.findOne({ name: view }, (err, template) => {
if (err) return reject(err);
if (!template) return reject(new Error('Template not found'));
let html = ejs.render(template, locals);
html = await email.juiceResources(html);
resolve(html);
});
});
}
});Absolute Path to Templates
As of v5.0.1+ we now support passing absolute paths to templates for rendering (per discussion in #320.
For both email.send and email.render, the template option passed can be a relative path or absolute:
Relative example:
email
.send({
template: 'mars',
message: {
to: 'elon@spacex.com'
},
locals: {
name: 'Elon'
}
})
.then(console.log)
.catch(console.error);Absolute example:
const path = require('path');
// ...
email
.send({
template: path.join(__dirname, 'some', 'folder', 'mars')
message: {
to: 'elon@spacex.com'
},
locals: {
name: 'Elon'
}
})
.then(console.log)
.catch(console.error);Open Email Previews in Firefox
The preview option can be a custom Object of options to pass along to opn's options.
Firefox example:
const email = new Email({
// ...
preview: {
app: 'firefox',
wait: false
}
});Options
For a list of all available options and defaults view the configuration object.
Plugins
You can use any nodemailer plugin. Simply pass an existing transport instance as config.transport.
You should add the nodemailer-base64-to-s3 plugin to convert base64 inline images to actual images stored on Amazon S3 and Cloudfront.
When doing so (as of v4.0.2+), you will need to adjust your email-templates configuration to pass images: true as such:
const email = new Email({
// ...
juiceResources: {
preserveImportant: true,
webResources: {
relativeTo: path.resolve('build'),
images: true // <--- set this as `true`
}
}
});We also highly recommend to add to your default config.locals the following:
- custom-fonts-in-emails - render any font in emails as an image w/retina support (no more Photoshop or Sketch exports!)
- font-awesome-assets - render any Font Awesome icon as an image in an email w/retina support (no more Photoshop or Sketch exports!)
V5 Breaking Changes
In version 5.x+, we changed the order of defaults being set. See #313 for more information. This allows you to override message options such as from (even if you have a global default from set).
V3 Breaking Changes
If you are upgrading from v2 or prior to v3, please note that the following breaking API changes occurred:
You need to have Node v6.4.0+, we recommend using nvm to manage your Node versions.
Instead of calling
const newsletter = new EmailTemplate(...args), you now callconst email = new Email(options).- The arguments you pass to the constructor have changed as well.
- Previously you'd pass
new EmailTemplate(templateDir, options). Now you will need to pass simply one object with a configuration as an argument to the constructor. - If your
templateDirpath is the "emails" folder in the root of your project (basically./emailsfolder) then you do not need to pass it at all since it is the default per the configuration object. - The previous value for
templateDircan be used as such:
-const newsletter = new EmailTemplate(templateDir); +const email = new Email({ + views: { root: templateDir } +});- Note that if you are inlining CSS, you should also make sure that the option for
juiceResources.webResources.relativeTois accurate.
Instead of calling
newsletter.render(locals, callback)you now callemail.render(template, locals). The return value ofemail.renderwhen invoked is aPromiseand does not accept a callback function.NOTE:
email-templatesv3 now has anemail.sendmethod (see basic usage example) which usesnodemailer; you should now useemail.sendinstead ofemail.render!-newsletter.render({}, (err, result) => { - if (err) return console.error(err); - console.log(result); -}); +email.render(template, {}).then(console.log).catch(console.error);Localized template directories are no longer supported. We now support i18n translations out of the box. See Localization for more info.
A new method
email.sendhas been added. This allows you to create a Nodemailer transport and send an email template all at once (it callsemail.renderinternally). See the Basic usage documentation above for an example.There are new options
options.sendandoptions.preview. Both are Boolean values and configured automatically based off the environment. Take a look at the configuration object. Note that you can optionally pass an Object topreviewoption, which gets passed along to opn's options.If you wish to send emails in development or test environment (disabled by default), set
options.sendtotrue.
Tip
Instead of having to configure this for yourself, you could just use Lad instead.
Related
- lad - Scaffold a Koa webapp and API framework for Node.js
- lass - Scaffold a modern boilerplate for Node.js
- cabin - Logging and analytics solution for Node.js, Lad, Koa, and Express
- forward-email - Free, encrypted, and open-source email forwarding service for custom domains
- lipo - Free image manipulation API service built on top of Sharp
Contributors
| Name | Website |
|---|---|
| Nick Baugh | http://niftylettuce.com |
License
7 years ago