# [**Email Templates**](https://github.com/forwardemail/email-templates) [](https://github.com/forwardemail/email-templates/actions/workflows/ci.yml) [](https://github.com/sindresorhus/xo) [](https://github.com/prettier/prettier) [](https://lass.js.org) [](LICENSE) 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](https://forwardemail.net/docs/how-to-javascript-contact-forms-node-js)** ## Table of Contents * [Install](#install) * [Preview](#preview) * [Usage](#usage) * [Debugging](#debugging) * [Basic](#basic) * [Attachments](#attachments) * [Automatic Inline CSS via Stylesheets](#automatic-inline-css-via-stylesheets) * [Render HTML and/or Text](#render-html-andor-text) * [Localization](#localization) * [Text-Only Email (no HTML)](#text-only-email-no-html) * [Prefix Subject Lines](#prefix-subject-lines) * [Custom Text Template](#custom-text-template) * [Custom Template Engine (e.g. EJS)](#custom-template-engine-eg-ejs) * [Custom Default Message Options](#custom-default-message-options) * [Custom Rendering (e.g. from a MongoDB database)](#custom-rendering-eg-from-a-mongodb-database) * [Absolute Path to Templates](#absolute-path-to-templates) * [Open Email Previews in Firefox](#open-email-previews-in-firefox) * [Options](#options) * [Tips](#tips) * [Purge unused CSS](#purge-unused-css) * [Optimized Pug Stylesheet Loading](#optimized-pug-stylesheet-loading) * [Plugins](#plugins) * [Breaking Changes](#breaking-changes) * [v12.0.0](#v1200) * [v11.0.0](#v1100) * [v10.0.0](#v1000) * [v9.0.0](#v900) * [v8.0.0](#v800) * [v7.0.0](#v700) * [v6.0.0](#v600) * [v5.0.0](#v500) * [v4.0.0](#v400) * [v3.0.0](#v300) * [Related](#related) * [Contributors](#contributors) * [License](#license) ## Install > 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][]: ```sh npm install email-templates preview-email pug ``` ## Preview 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](#open-email-previews-in-firefox). ## Usage ### Debugging #### Environment Flag If you run into any issues with configuration, files, templates, locals, etc, then you can use the `NODE_DEBUG` environment flag: ```sh NODE_DEBUG=email-templates node app.js ``` This 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`: ```js 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 `transport` option 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 `development` or `test` environments, set `options.send` to `true`. ```js 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: ```sh . ├── app.js └── emails └── mars ├── html.pug └── subject.pug ``` And the contents of the `pug` files are: > `html.pug`: ```pug p Hi #{name}, p Welcome to Mars, the red planet. ``` > `subject.pug`: ```pug = `Hi ${name}, welcome to Mars` ``` ### Attachments Please reference [Nodemailer's attachment documentation][attachments] for further reference. > If you want to set default attachments sent with every email: ```js 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: ```js 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); ``` ### Automatic Inline CSS via Stylesheets Simply include the path or URL to the stylesheet in your template's `
`: ```pug link(rel="stylesheet", href="/css/app.css", data-inline) ``` This will look for the file `/css/app.css` in the `build/` folder. Also see [Optimized Pug Stylesheet Loading](#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: ```js const email = new Email({ //{{ 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: ```js 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`: ```handlebars{{ $t "greetings" firstname="Marcus" }}
{{ $t "welcome_message" }}
``` ### 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. ```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: { 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**): ```js const Email = require('email-templates'); const env = process.env.NODE_ENV || 'development'; const email = new Email({ message: { from: 'test@example.com' }, transport: { jsonTransport: true }, subjectPrefix: env === 'production' ? false : `[${env.toUpperCase()}] `; // <--- HERE }); ``` ### Custom Text Template > By default we use `html-to-text` to generate a plaintext version and attach it as `message.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. ```js const Email = require('email-templates'); const email = new Email({ message: { from: 'test@example.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`: ```pug | Hi #{name}, | Welcome to Mars, the red planet. ``` ### Custom Template Engine (e.g. EJS) 1. Install your desired template engine (e.g. [EJS][]) [npm][]: ```sh npm install ejs ``` 2. Set the extension in options and send an email ```js const Email = require('email-templates'); const email = new Email({ message: { from: 'test@example.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](https://nodemailer.com/message/). > Here's an example showing how to set a default custom header and a list unsubscribe header: ```js const Email = require('email-templates'); const email = new Email({ message: { from: 'test@example.com', headers: { 'X-Some-Custom-Thing': 'Some-Value' }, list: { unsubscribe: 'https://example.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: ```js 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](https://github.com/forwardemail/email-templates/issues/320). For both `email.send` and `email.render`, the `template` option passed can be a relative path or absolute: > Relative example: ```js email .send({ template: 'mars', message: { to: 'elon@spacex.com' }, locals: { name: 'Elon' } }) .then(console.log) .catch(console.error); ``` > Absolute example: ```js 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 [open's options][open-options]. > Firefox example: ```js const email = new Email({ // ... preview: { open: { app: 'firefox', wait: false } } }); ``` ## Options For a list of all available options and defaults [view the configuration object](src/index.js), or reference the list below: * `views` (Object) * `root` (String) - defaults to the current working directory's "emails" folder via `path.resolve('emails')` * `options` (Object) * `extension` (String) - defaults to `'pug'`, and is the default file extension for templates * `map` (Object) - a template file extension mapping, defaults to `{ hbs: 'handlebars', njk: 'nunjucks' }` (this is useful if you use different file extension naming conventions) * `engineSource` (Object) - the default template engine source, defaults to [@ladjs/consolidate][consolidate] * `locals` (Object) - locals to pass to templates for rendering * `cache` (Boolean) - defaults to `false` for `development` and `test` environments, and `true` for all others (via `process.env.NODE_ENV`), whether or not to cache templates * `pretty` (Boolean) - defaults to `true`, but is automatically set to `false` for subject templates and text-based emails * `message` (Object) - default [Nodemailer message object][nodemailer-message-object] for messages to inherit (defaults to an empty object `{}`) * `send` (Boolean) - whether or not to send emails, defaults to `false` for `development` and `test` environments, and `true` for all others (via `process.env.NODE_ENV`) (**NOTE: IF YOU ARE NOT USING `NODE_ENV` YOU WILL NEED TO MANUALLY SET THIS TO `true`**) * `preview` (Boolean or Object) - whether or not to preview emails using [preview-email][], defaults to `false` unless the environment is `development` (via `process.env.NODE_ENV`) – if you wish to disable the iOS Simulator then pass `{ openSimulator: false }` * `i18n` (Boolean or Object) - translation support for email templates, this accepts an I18N configuration object (defaults to `false`, which means it is disabled) which is passed along to [@ladjs/i18n][i18n] – see [Localization](#localization) example for more insight * `render` (Function) - defaults to a stable function that accepts two argument, `view` (String) and `locals` (Object) - you should not need to set this unless you have a need for custom rendering (see [Custom Rendering (e.g. from a MongoDB database)](#custom-rendering-eg-from-a-mongodb-database)) * `customRender` (Boolean) - defaults to `false`, unless you pass your own `render` function, and in that case it will be automatically set to `true` * `textOnly` (Boolean) - whether or not to force text-only rendering of a template and disregard the template folder (defaults to `false`) * `htmlToText` (Object) - configuration object for [html-to-text][] * `ignoreImage` (Boolean) - defaults to `true` * `subjectPrefix` (Boolean or String) - defaults to `false`, but if set to a string it will use that string as a prefix for your emails' subjects * `juice` (Boolean) - whether or not to use [juice][] when rendering templates (defaults to `true`) (note that if you have a custom rendering function you will need to implement [juice][] in it yourself) * `juiceResources` (Object) - options to pass to `juice.juiceResources` method (only used if `juice` option is set to `true`, see [juice's][juice] API for more information * `applyStyleTags` (Boolean) - defaults to `false` (as of v11, since modern browsers now support `