├── .commitlintrc.js ├── .editorconfig ├── .eslintignore ├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .lintstagedrc.js ├── .npmrc ├── .prettierrc.js ├── .remarkignore ├── .remarkrc.js ├── .xo-config.js ├── CNAME ├── LICENSE ├── README.md ├── config.js ├── examples └── sendgrid.js ├── favicon.ico ├── index.html ├── index.js ├── package.json └── test ├── fixtures ├── emails │ ├── style.css │ ├── test-ejs │ │ ├── html.ejs │ │ ├── subject.ejs │ │ └── text.ejs │ ├── test-html-only │ │ ├── html.pug │ │ └── subject.pug │ ├── test-text-only │ │ ├── subject.pug │ │ └── text.pug │ └── test │ │ ├── html.pug │ │ ├── subject.pug │ │ └── text.pug └── filename.png └── test.js /.commitlintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'] 3 | }; 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | !.*.js 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | build: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: 11 | - ubuntu-latest 12 | node_version: 13 | - 14 14 | - 16 15 | - 18 16 | name: Node ${{ matrix.node_version }} on ${{ matrix.os }} 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Setup node 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: ${{ matrix.node_version }} 23 | - name: Install dependencies 24 | run: npm install 25 | - name: Run tests 26 | run: npm run test 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | .idea 4 | node_modules 5 | coverage 6 | .nyc_output 7 | locales/ 8 | package-lock.json 9 | yarn.lock 10 | 11 | Thumbs.db 12 | tmp/ 13 | temp/ 14 | *.lcov 15 | .env -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install lint-staged && npm test 5 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '*.md': (filenames) => filenames.map((filename) => `remark ${filename} -qfo`), 3 | 'package.json': 'fixpack', 4 | '*.js': 'xo --fix' 5 | }; 6 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | bracketSpacing: true, 4 | trailingComma: 'none' 5 | }; 6 | -------------------------------------------------------------------------------- /.remarkignore: -------------------------------------------------------------------------------- 1 | test/snapshots/**/*.md 2 | -------------------------------------------------------------------------------- /.remarkrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ['preset-github'] 3 | }; 4 | -------------------------------------------------------------------------------- /.xo-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | prettier: true, 3 | space: true, 4 | extends: ['xo-lass'], 5 | ignore: ['config.js'], 6 | rules: { 7 | 'unicorn/prefer-top-level-await': 'warn', 8 | 'unicorn/prefer-node-protocol': 'off' 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | email-templates.js.org 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nick Baugh (http://niftylettuce.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [**Email Templates**](https://github.com/forwardemail/email-templates) 2 | 3 | [![build status](https://github.com/forwardemail/email-templates/actions/workflows/ci.yml/badge.svg)](https://github.com/forwardemail/email-templates/actions/workflows/ci.yml) 4 | [![code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo) 5 | [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) 6 | [![made with lass](https://img.shields.io/badge/made_with-lass-95CC28.svg)](https://lass.js.org) 7 | [![license](https://img.shields.io/github/license/forwardemail/email-templates.svg)](LICENSE) 8 | 9 | Create, [preview][preview-email] (browser/iOS Simulator), and send custom email templates for [Node.js][node]. Made for [Forward Email][forward-email] and [Lad][]. 10 | 11 | > **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)** 12 | 13 | 14 | ## Table of Contents 15 | 16 | * [Install](#install) 17 | * [Preview](#preview) 18 | * [Usage](#usage) 19 | * [Debugging](#debugging) 20 | * [Basic](#basic) 21 | * [Attachments](#attachments) 22 | * [Automatic Inline CSS via Stylesheets](#automatic-inline-css-via-stylesheets) 23 | * [Render HTML and/or Text](#render-html-andor-text) 24 | * [Localization](#localization) 25 | * [Text-Only Email (no HTML)](#text-only-email-no-html) 26 | * [Prefix Subject Lines](#prefix-subject-lines) 27 | * [Custom Text Template](#custom-text-template) 28 | * [Custom Template Engine (e.g. EJS)](#custom-template-engine-eg-ejs) 29 | * [Custom Default Message Options](#custom-default-message-options) 30 | * [Custom Rendering (e.g. from a MongoDB database)](#custom-rendering-eg-from-a-mongodb-database) 31 | * [Absolute Path to Templates](#absolute-path-to-templates) 32 | * [Open Email Previews in Firefox](#open-email-previews-in-firefox) 33 | * [Options](#options) 34 | * [Tips](#tips) 35 | * [Purge unused CSS](#purge-unused-css) 36 | * [Optimized Pug Stylesheet Loading](#optimized-pug-stylesheet-loading) 37 | * [Plugins](#plugins) 38 | * [Breaking Changes](#breaking-changes) 39 | * [v12.0.0](#v1200) 40 | * [v11.0.0](#v1100) 41 | * [v10.0.0](#v1000) 42 | * [v9.0.0](#v900) 43 | * [v8.0.0](#v800) 44 | * [v7.0.0](#v700) 45 | * [v6.0.0](#v600) 46 | * [v5.0.0](#v500) 47 | * [v4.0.0](#v400) 48 | * [v3.0.0](#v300) 49 | * [Related](#related) 50 | * [Contributors](#contributors) 51 | * [License](#license) 52 | 53 | 54 | ## Install 55 | 56 | > 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. 57 | 58 | [npm][]: 59 | 60 | ```sh 61 | npm install email-templates preview-email pug 62 | ``` 63 | 64 | 65 | ## Preview 66 | 67 | 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. 68 | 69 | 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. 70 | 71 | 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' } }`). 72 | 73 | See the example below for [Open Email Previews in Firefox](#open-email-previews-in-firefox). 74 | 75 | 76 | ## Usage 77 | 78 | ### Debugging 79 | 80 | #### Environment Flag 81 | 82 | If you run into any issues with configuration, files, templates, locals, etc, then you can use the `NODE_DEBUG` environment flag: 83 | 84 | ```sh 85 | NODE_DEBUG=email-templates node app.js 86 | ``` 87 | 88 | This will output to the console all debug statements in our codebase for this package. 89 | 90 | #### Inspect Message 91 | 92 | As of v3.6.1 you can now inspect the message passed to `nodemailer.sendMail` internally. 93 | 94 | In the response object from `email.send`, you have access to `res.originalMessage`: 95 | 96 | ```js 97 | email 98 | .send({ 99 | template: 'mars', 100 | message: { 101 | to: 'elon@spacex.com' 102 | }, 103 | locals: { 104 | name: 'Elon' 105 | } 106 | }) 107 | .then(res => { 108 | console.log('res.originalMessage', res.originalMessage) 109 | }) 110 | .catch(console.error); 111 | ``` 112 | 113 | ### Basic 114 | 115 | > 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][]). 116 | > 117 | > If you want to send emails in `development` or `test` environments, set `options.send` to `true`. 118 | 119 | ```js 120 | const Email = require('email-templates'); 121 | 122 | const email = new Email({ 123 | message: { 124 | from: 'test@example.com' 125 | }, 126 | // uncomment below to send emails in development/test env: 127 | // send: true 128 | transport: { 129 | jsonTransport: true 130 | } 131 | }); 132 | 133 | email 134 | .send({ 135 | template: 'mars', 136 | message: { 137 | to: 'elon@spacex.com' 138 | }, 139 | locals: { 140 | name: 'Elon' 141 | } 142 | }) 143 | .then(console.log) 144 | .catch(console.error); 145 | ``` 146 | 147 | The example above assumes you have the following directory structure: 148 | 149 | ```sh 150 | . 151 | ├── app.js 152 | └── emails 153 | └── mars 154 | ├── html.pug 155 | └── subject.pug 156 | ``` 157 | 158 | And the contents of the `pug` files are: 159 | 160 | > `html.pug`: 161 | 162 | ```pug 163 | p Hi #{name}, 164 | p Welcome to Mars, the red planet. 165 | ``` 166 | 167 | > `subject.pug`: 168 | 169 | ```pug 170 | = `Hi ${name}, welcome to Mars` 171 | ``` 172 | 173 | ### Attachments 174 | 175 | Please reference [Nodemailer's attachment documentation][attachments] for further reference. 176 | 177 | > If you want to set default attachments sent with every email: 178 | 179 | ```js 180 | const Email = require('email-templates'); 181 | 182 | const email = new Email({ 183 | message: { 184 | from: 'test@example.com', 185 | attachments: [ 186 | { 187 | filename: 'text1.txt', 188 | content: 'hello world!' 189 | } 190 | ] 191 | } 192 | }); 193 | 194 | email 195 | .send({ 196 | template: 'mars', 197 | message: { 198 | to: 'elon@spacex.com' 199 | }, 200 | locals: { 201 | name: 'Elon' 202 | } 203 | }) 204 | .then(console.log) 205 | .catch(console.error); 206 | ``` 207 | 208 | > If you want to set attachments sent individually: 209 | 210 | ```js 211 | const Email = require('email-templates'); 212 | 213 | const email = new Email({ 214 | message: { 215 | from: 'test@example.com' 216 | }, 217 | transport: { 218 | jsonTransport: true 219 | } 220 | }); 221 | 222 | email 223 | .send({ 224 | template: 'mars', 225 | message: { 226 | to: 'elon@spacex.com', 227 | attachments: [ 228 | { 229 | filename: 'text1.txt', 230 | content: 'hello world!' 231 | } 232 | ] 233 | }, 234 | locals: { 235 | name: 'Elon' 236 | } 237 | }) 238 | .then(console.log) 239 | .catch(console.error); 240 | ``` 241 | 242 | ### Automatic Inline CSS via Stylesheets 243 | 244 | Simply include the path or URL to the stylesheet in your template's ``: 245 | 246 | ```pug 247 | link(rel="stylesheet", href="/css/app.css", data-inline) 248 | ``` 249 | 250 | This will look for the file `/css/app.css` in the `build/` folder. Also see [Optimized Pug Stylesheet Loading](#optimized-pug-stylesheet-loading) below. 251 | 252 | If this asset is in another folder, then you will need to modify the default options when creating an `Email` instance: 253 | 254 | ```js 255 | const email = new Email({ 256 | // 257 | juice: true, 258 | // Override juice global settings 259 | juiceSettings: { 260 | tableElements: ['TABLE'] 261 | }, 262 | juiceResources: { 263 | // set this to `true` (since as of v11 it is `false` by default) 264 | applyStyleTags: true, // <------------ you need to set this to `true` 265 | webResources: { 266 | // 267 | // this is the relative directory to your CSS/image assets 268 | // and its default path is `build/`: 269 | // 270 | // e.g. if you have the following in the ` of your template: 271 | // `` 272 | // then this assumes that the file `build/style.css` exists 273 | // 274 | relativeTo: path.resolve('build') 275 | // 276 | // but you might want to change it to something like: 277 | // relativeTo: path.join(__dirname, '..', 'assets') 278 | // (so that you can re-use CSS/images that are used in your web-app) 279 | // 280 | } 281 | } 282 | }); 283 | ``` 284 | 285 | ### Render HTML and/or Text 286 | 287 | If you don't need this module to send your email, you can still use it to render HTML and/or text templates. 288 | 289 | Simply use the `email.render(view, locals)` method we expose (it's the same method that `email.send` uses internally). 290 | 291 | > If you need to render a specific email template file (e.g. the HTML version): 292 | 293 | ```js 294 | const Email = require('email-templates'); 295 | 296 | const email = new Email(); 297 | 298 | email 299 | .render('mars/html', { 300 | name: 'Elon' 301 | }) 302 | .then(console.log) 303 | .catch(console.error); 304 | ``` 305 | 306 | The example above assumes you have the following directory structure (note that this example would only render the `html.pug` file): 307 | 308 | ```sh 309 | . 310 | ├── app.js 311 | └── emails 312 | └── mars 313 | ├── html.pug 314 | ├── text.pug 315 | └── subject.pug 316 | ``` 317 | 318 | The Promise for `email.render` resolves with a String (the HTML or text rendered). 319 | 320 | > If you need pass juiceResources in render function, with this option you don't need create Email instance every time 321 | 322 | ```js 323 | const Email = require('email-templates'); 324 | 325 | const email = new Email(); 326 | 327 | email 328 | .render({ 329 | path: 'mars/html', 330 | juiceResources: { 331 | webResources: { 332 | // view folder path, it will get css from `mars/style.css` 333 | relativeTo: path.resolve('mars') 334 | } 335 | } 336 | }, { 337 | name: 'Elon' 338 | }) 339 | .then(console.log) 340 | .catch(console.error); 341 | ``` 342 | 343 | 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 344 | 345 | ```sh 346 | . 347 | ├── app.js 348 | └── emails 349 | └── mars 350 | ├── html.pug 351 | ├── text.pug 352 | ├── subject.pug 353 | └── style.css 354 | ``` 355 | 356 | The Promise for `email.render` resolves with a String (the HTML or text rendered). 357 | 358 | > If you need to render all available template files for a given email template (e.g. `html.pug`, `text.pug`, and `subject.pug` – you can use `email.renderAll` (this is the method that `email.send` uses). 359 | 360 | ```js 361 | const Email = require('email-templates'); 362 | 363 | const email = new Email(); 364 | 365 | email 366 | .renderAll('mars', { 367 | name: 'Elon' 368 | }) 369 | .then(console.log) 370 | .catch(console.error); 371 | ``` 372 | 373 | > If you need to render multiple, specific templates at once (but not all email templates available), then you can use `Promise.all` in combination with `email.render`: 374 | 375 | ```js 376 | const Email = require('email-templates'); 377 | 378 | const email = new Email(); 379 | const locals = { name: 'Elon' }; 380 | 381 | Promise 382 | .all([ 383 | email.render('mars/html', locals), 384 | email.render('mars/text', locals) 385 | ]) 386 | .then(([ html, text ]) => { 387 | console.log('html', html); 388 | console.log('text', text); 389 | }) 390 | .catch(console.error); 391 | ``` 392 | 393 | ### Localization 394 | 395 | 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). 396 | 397 | > 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. 398 | 399 | ```js 400 | const Email = require('email-templates'); 401 | 402 | const email = new Email({ 403 | message: { 404 | from: 'test@example.com' 405 | }, 406 | transport: { 407 | jsonTransport: true 408 | }, 409 | i18n: {} // <------ HERE 410 | }); 411 | 412 | email 413 | .send({ 414 | template: 'mars', 415 | message: { 416 | to: 'elon@spacex.com' 417 | }, 418 | locals: { 419 | locale: 'en', // <------ CUSTOMIZE LOCALE HERE (defaults to `i18n.defaultLocale` - `en`) 420 | // is your user french? 421 | // locale: 'fr', 422 | name: 'Elon' 423 | } 424 | }) 425 | .then(console.log) 426 | .catch(console.error); 427 | ``` 428 | 429 | Then slightly modify your templates to use localization functions. 430 | 431 | > `html.pug`: 432 | 433 | ```pug 434 | p= `${t('Hi')} ${name},` 435 | p= t('Welcome to Mars, the red planet.') 436 | ``` 437 | 438 | > `subject.pug`: 439 | 440 | ```pug 441 | p= `${t('Hi')} ${name}, ${t('welcome to Mars')}` 442 | ``` 443 | 444 | Note that if you use [Lad][], you have a built-in filter called `translate`: 445 | 446 | ```pug 447 | p: :translate(locale) Welcome to Mars, the red planet. 448 | ``` 449 | 450 | #### Localization using Handlebars template engine 451 | 452 | If you are using handlebars and you are using localization files with named values, you will quickly see that 453 | there is no way to properly call the `t` function in your template and specify named values. 454 | 455 | If, for example you have this in your translation file: 456 | 457 | ```json 458 | { 459 | "greetings": "Hi {{ firstname }}", 460 | "welcome_message": "Welcome to Mars, the red planet." 461 | } 462 | ``` 463 | 464 | And you would like to use it in your template like this: 465 | 466 | > `html.hbs`: 467 | 468 | ```handlebars 469 |

{{ t "greetings" firstname="Marcus" }}

470 |

{{ t "welcome_message" }}

471 | ``` 472 | 473 | This would not work because the second argument sent by handlebars to the function would be a handlebar helper 474 | options object instead of just the named values. 475 | 476 | A possible workaround you can use is to introduce your own translation helper in your template locals: 477 | 478 | ```js 479 | email 480 | .send({ 481 | template: 'mars', 482 | message: { 483 | to: 'elon@spacex.com' 484 | }, 485 | locals: { 486 | locale: 'en', // <------ CUSTOMIZE LOCALE HERE (defaults to `i18n.defaultLocale` - `en`) 487 | // is your user french? 488 | // locale: 'fr', 489 | name: 'Elon', 490 | $t(key, options) { 491 | // <------ THIS IS OUR OWN TRANSLATION HELPER 492 | return options.data.root.t( 493 | { phrase: key, locale: options.data.root.locale }, 494 | options.hash 495 | ); 496 | } 497 | } 498 | }) 499 | .then(console.log) 500 | .catch(console.error); 501 | ``` 502 | 503 | Then slightly modify your templates to use your own translation helper functions. 504 | 505 | > `html.hbs`: 506 | 507 | ```handlebars 508 |

{{ $t "greetings" firstname="Marcus" }}

509 |

{{ $t "welcome_message" }}

510 | ``` 511 | 512 | ### Text-Only Email (no HTML) 513 | 514 | If you wish to have only a text-based version of your email you can simply pass the option `textOnly: true`. 515 | 516 | Regardless if you use the `htmlToText` option or not (see next example), it will still render only a text-based version. 517 | 518 | ```js 519 | const Email = require('email-templates'); 520 | 521 | const email = new Email({ 522 | message: { 523 | from: 'test@example.com' 524 | }, 525 | transport: { 526 | jsonTransport: true 527 | }, 528 | textOnly: true // <----- HERE 529 | }); 530 | 531 | email 532 | .send({ 533 | template: 'mars', 534 | message: { 535 | to: 'elon@spacex.com' 536 | }, 537 | locals: { 538 | name: 'Elon' 539 | } 540 | }) 541 | .then(console.log) 542 | .catch(console.error); 543 | ``` 544 | 545 | ### Prefix Subject Lines 546 | 547 | You can pass an option to prefix subject lines with a string, which is super useful for deciphering development / staging / production environment emails. 548 | 549 | For example, you could make it so on non-production environments the email is prefixed with a `[DEVELOPMENT] Some Subject Line Here`. 550 | 551 | 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. 552 | 553 | 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**): 554 | 555 | ```js 556 | const Email = require('email-templates'); 557 | 558 | const env = process.env.NODE_ENV || 'development'; 559 | 560 | const email = new Email({ 561 | message: { 562 | from: 'test@example.com' 563 | }, 564 | transport: { 565 | jsonTransport: true 566 | }, 567 | subjectPrefix: env === 'production' ? false : `[${env.toUpperCase()}] `; // <--- HERE 568 | }); 569 | ``` 570 | 571 | ### Custom Text Template 572 | 573 | > By default we use `html-to-text` to generate a plaintext version and attach it as `message.text`. 574 | 575 | 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`. 576 | 577 | You may also set `config.htmlToText: false` to force the usage of the `text` template file. 578 | 579 | ```js 580 | const Email = require('email-templates'); 581 | 582 | const email = new Email({ 583 | message: { 584 | from: 'test@example.com' 585 | }, 586 | transport: { 587 | jsonTransport: true 588 | }, 589 | htmlToText: false // <----- HERE 590 | }); 591 | 592 | email 593 | .send({ 594 | template: 'mars', 595 | message: { 596 | to: 'elon@spacex.com' 597 | }, 598 | locals: { 599 | name: 'Elon' 600 | } 601 | }) 602 | .then(console.log) 603 | .catch(console.error); 604 | ``` 605 | 606 | > `text.pug`: 607 | 608 | ```pug 609 | | Hi #{name}, 610 | | Welcome to Mars, the red planet. 611 | ``` 612 | 613 | ### Custom Template Engine (e.g. EJS) 614 | 615 | 1. Install your desired template engine (e.g. [EJS][]) 616 | 617 | [npm][]: 618 | 619 | ```sh 620 | npm install ejs 621 | ``` 622 | 623 | 2. Set the extension in options and send an email 624 | 625 | ```js 626 | const Email = require('email-templates'); 627 | 628 | const email = new Email({ 629 | message: { 630 | from: 'test@example.com' 631 | }, 632 | transport: { 633 | jsonTransport: true 634 | }, 635 | views: { 636 | options: { 637 | extension: 'ejs' // <---- HERE 638 | } 639 | } 640 | }); 641 | ``` 642 | 643 | ### Custom Default Message Options 644 | 645 | You can configure your Email instance to have default message options, such as a default "From", an unsubscribe header, etc. 646 | 647 | For a list of all available message options and fields see [the Nodemailer message reference](https://nodemailer.com/message/). 648 | 649 | > Here's an example showing how to set a default custom header and a list unsubscribe header: 650 | 651 | ```js 652 | const Email = require('email-templates'); 653 | 654 | const email = new Email({ 655 | message: { 656 | from: 'test@example.com', 657 | headers: { 658 | 'X-Some-Custom-Thing': 'Some-Value' 659 | }, 660 | list: { 661 | unsubscribe: 'https://example.com/unsubscribe' 662 | } 663 | }, 664 | transport: { 665 | jsonTransport: true 666 | } 667 | }); 668 | ``` 669 | 670 | ### Custom Rendering (e.g. from a MongoDB database) 671 | 672 | You can pass a custom `config.render` function which accepts two arguments `view` and `locals` and must return a `Promise`. 673 | 674 | 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. 675 | 676 | If you wanted to read a stored EJS template from MongoDB, you could do something like: 677 | 678 | ```js 679 | const ejs = require('ejs'); 680 | 681 | const email = new Email({ 682 | // ... 683 | render: (view, locals) => { 684 | return new Promise((resolve, reject) => { 685 | // this example assumes that `template` returned 686 | // is an ejs-based template string 687 | // view = `${template}/html` or `${template}/subject` or `${template}/text` 688 | db.templates.findOne({ name: view }, (err, template) => { 689 | if (err) return reject(err); 690 | if (!template) return reject(new Error('Template not found')); 691 | let html = ejs.render(template, locals); 692 | html = await email.juiceResources(html); 693 | resolve(html); 694 | }); 695 | }); 696 | } 697 | }); 698 | ``` 699 | 700 | ### Absolute Path to Templates 701 | 702 | 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). 703 | 704 | For both `email.send` and `email.render`, the `template` option passed can be a relative path or absolute: 705 | 706 | > Relative example: 707 | 708 | ```js 709 | email 710 | .send({ 711 | template: 'mars', 712 | message: { 713 | to: 'elon@spacex.com' 714 | }, 715 | locals: { 716 | name: 'Elon' 717 | } 718 | }) 719 | .then(console.log) 720 | .catch(console.error); 721 | ``` 722 | 723 | > Absolute example: 724 | 725 | ```js 726 | const path = require('path'); 727 | 728 | // ... 729 | 730 | email 731 | .send({ 732 | template: path.join(__dirname, 'some', 'folder', 'mars') 733 | message: { 734 | to: 'elon@spacex.com' 735 | }, 736 | locals: { 737 | name: 'Elon' 738 | } 739 | }) 740 | .then(console.log) 741 | .catch(console.error); 742 | ``` 743 | 744 | ### Open Email Previews in Firefox 745 | 746 | The `preview` option can be a custom Object of options to pass along to [open's options][open-options]. 747 | 748 | > Firefox example: 749 | 750 | ```js 751 | const email = new Email({ 752 | // ... 753 | preview: { 754 | open: { 755 | app: 'firefox', 756 | wait: false 757 | } 758 | } 759 | }); 760 | ``` 761 | 762 | 763 | ## Options 764 | 765 | For a list of all available options and defaults [view the configuration object](src/index.js), or reference the list below: 766 | 767 | * `views` (Object) 768 | * `root` (String) - defaults to the current working directory's "emails" folder via `path.resolve('emails')` 769 | * `options` (Object) 770 | * `extension` (String) - defaults to `'pug'`, and is the default file extension for templates 771 | * `map` (Object) - a template file extension mapping, defaults to `{ hbs: 'handlebars', njk: 'nunjucks' }` (this is useful if you use different file extension naming conventions) 772 | * `engineSource` (Object) - the default template engine source, defaults to [@ladjs/consolidate][consolidate] 773 | * `locals` (Object) - locals to pass to templates for rendering 774 | * `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 775 | * `pretty` (Boolean) - defaults to `true`, but is automatically set to `false` for subject templates and text-based emails 776 | * `message` (Object) - default [Nodemailer message object][nodemailer-message-object] for messages to inherit (defaults to an empty object `{}`) 777 | * `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`**) 778 | * `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 }` 779 | * `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 780 | * `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)) 781 | * `customRender` (Boolean) - defaults to `false`, unless you pass your own `render` function, and in that case it will be automatically set to `true` 782 | * `textOnly` (Boolean) - whether or not to force text-only rendering of a template and disregard the template folder (defaults to `false`) 783 | * `htmlToText` (Object) - configuration object for [html-to-text][] 784 | * `ignoreImage` (Boolean) - defaults to `true` 785 | * `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 786 | * `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) 787 | * `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 788 | * `applyStyleTags` (Boolean) - defaults to `false` (as of v11, since modern browsers now support `