Create, [preview][preview-email] (browser/iOS Simulator), and send custom email templates for [Node.js][node]. Made for [Forward Email][forward-email] and [Lad][].
Need to send emails that land in the inbox instead of spam folder? Click here to learn how to send JavaScript contact forms and more with Node.js
By default we recommend [pug][] for your template engine, but you can use [any template engine][supported-engines]. Note that [preview-email][] is an optional dependency and is extremely helpful for rendering development previews of your emails automatically in your browser.
[npm][]:
npm install email-templates preview-email pug
We've added [preview-email][] by default to this package. This package allows you to preview emails in the browser and in the iOS Simulator.
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 [open's options][open-options] (e.g. preview: { open: { app: 'firefox' } }).
See the example below for Open Email Previews in Firefox.
If you run into any issues with configuration, files, templates, locals, etc, then you can use the NODE_DEBUG environment flag:
NODE_DEBUG=email-templates node app.js
This will output to the console all debug statements in our codebase for this package.
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);
You can swap the
transportoption with a [Nodemailer transport][nodemailer-transport] configuration object or transport instance. We highly recommend using [Forward Email][forward-email] 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: 'test@example.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.pug
And 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`
Please reference [Nodemailer's attachment documentation][attachments] 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: 'test@example.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: 'test@example.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);
Simply include the path or URL to the stylesheet in your template's <head>:
link(rel="stylesheet", href="https://github.com/forwardemail/email-templates/raw/v13.0.1/css/app.css", data-inline)
This will look for the file /css/app.css in the build/ folder. Also see Optimized Pug Stylesheet Loading below.
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,
// Override juice global settings <https://github.com/Automattic/juice#juicecodeblocks>
juiceSettings: {
tableElements: ['TABLE']
},
juiceResources: {
// set this to `true` (since as of v11 it is `false` by default)
applyStyleTags: true, // <------------ you need to set this to `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="https://github.com/forwardemail/email-templates/raw/v13.0.1/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)
//
}
}
});
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.pug
The Promise for email.render resolves with a String (the HTML or text rendered).
If you need pass juiceResources in render function, with this option you don't need create Email instance every time
const Email = require('email-templates');
const email = new Email();
email
.render({
path: 'mars/html',
juiceResources: {
webResources: {
// view folder path, it will get css from `mars/style.css`
relativeTo: path.resolve('mars')
}
}
}, {
name: 'Elon'
})
.then(console.log)
.catch(console.error);
The example above will be useful when you have a structure like this, this will be useful when you have a separate CSS file for every template
.
├── app.js
└── emails
└── mars
├── html.pug
├── text.pug
├── subject.pug
└── style.css
The 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);
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][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: 'test@example.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.
If you are using handlebars and you are using localization files with named values, you will quickly see that
there is no way to properly call the t function in your template and specify named values.
If, for example you have this in your translation file:
{
"greetings": "Hi {{ firstname }}",
"welcome_message": "Welcome to Mars, the red planet."
}
And you would like to use it in your template like this:
html.hbs:
{{ t "greetings" firstname="Marcus" }}
{{ t "welcome_message" }}
This would not work because the second argument sent by handlebars to the function would be a handlebar helper options object instead of just the named values.
A possible workaround you can use is to introduce your own translation helper in your template locals:
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',
$t(key, options) {
// <------ THIS IS OUR OWN TRANSLATION HELPER
return options.data.root.t(
{ phrase: key, locale: options.data.root.locale },
options.hash
);
}
}
})
.then(console.log)
.catch(console.error);
Then slightly modify your templates to use your own translation helper functions.
html.hbs:
{{ $t "greetings" firstname="Marcus" }}
{{ $t "welcome_message" }}
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.
```js const Email = require('email-templates');
const email = new Email({ message: { from: 'test@example.com' }, transport: { jsonTransport: true }, textOnly: true // <----- HERE });
email .send({ template: 'mars', message: { to: 'elon@spacex.com' }, locals: {
$ claude mcp add email-templates \
-- python -m otcore.mcp_server <graph>