{"id":13455000,"url":"https://github.com/forwardemail/email-templates","last_synced_at":"2026-02-14T01:02:04.759Z","repository":{"id":3611038,"uuid":"4676086","full_name":"forwardemail/email-templates","owner":"forwardemail","description":"Create, preview (browser/iOS Simulator), and send custom email templates for Node.js.  Made for @forwardemail, @ladjs, @cabinjs, @spamscanner, and @breejs.","archived":false,"fork":false,"pushed_at":"2025-12-31T18:11:37.000Z","size":2444,"stargazers_count":3725,"open_issues_count":0,"forks_count":338,"subscribers_count":63,"default_branch":"master","last_synced_at":"2026-02-04T08:53:06.064Z","etag":null,"topics":["css","custom","ejs","email","inline","inline-css","juice","mailchimp","mandrill","node","nodejs","nodemailer","postmark","pug","responsive","sendgrid","template","template-engine","templates","webresource"],"latest_commit_sha":null,"homepage":"https://forwardemail.net/docs/send-emails-with-node-js-javascript","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/forwardemail.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2012-06-15T14:23:32.000Z","updated_at":"2026-02-02T05:35:26.000Z","dependencies_parsed_at":"2026-02-14T01:01:47.607Z","dependency_job_id":null,"html_url":"https://github.com/forwardemail/email-templates","commit_stats":{"total_commits":236,"total_committers":19,"mean_commits":"12.421052631578947","dds":0.211864406779661,"last_synced_commit":"bf6c43425778a09594558d9399e51de3c10cd2b9"},"previous_names":["niftylettuce/node-email-templates","niftylettuce/email-templates"],"tags_count":123,"template":false,"template_full_name":null,"purl":"pkg:github/forwardemail/email-templates","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forwardemail%2Femail-templates","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forwardemail%2Femail-templates/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forwardemail%2Femail-templates/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forwardemail%2Femail-templates/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/forwardemail","download_url":"https://codeload.github.com/forwardemail/email-templates/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forwardemail%2Femail-templates/sbom","scorecard":{"id":407596,"data":{"date":"2025-08-11","repo":{"name":"github.com/forwardemail/email-templates","commit":"e5cf1e6c0b972270d36bc80b16e8b26cc029e85f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.2,"checks":[{"name":"Maintained","score":2,"reason":"3 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 2/28 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/forwardemail/email-templates/ci.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/forwardemail/email-templates/ci.yml/master?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/ci.yml:25","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: github.com/forwardemail/.github/SECURITY.md:1","Info: Found linked content: github.com/forwardemail/.github/SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: github.com/forwardemail/.github/SECURITY.md:1","Info: Found text in security policy: github.com/forwardemail/.github/SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 4 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T21:47:06.658Z","repository_id":3611038,"created_at":"2025-08-18T21:47:06.658Z","updated_at":"2025-08-18T21:47:06.658Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29427616,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-13T22:20:51.549Z","status":"ssl_error","status_checked_at":"2026-02-13T22:20:49.838Z","response_time":78,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css","custom","ejs","email","inline","inline-css","juice","mailchimp","mandrill","node","nodejs","nodemailer","postmark","pug","responsive","sendgrid","template","template-engine","templates","webresource"],"created_at":"2024-07-31T08:01:00.164Z","updated_at":"2026-02-14T01:02:04.747Z","avatar_url":"https://github.com/forwardemail.png","language":"JavaScript","readme":"# [**Email Templates**](https://github.com/forwardemail/email-templates)\n\n[![build status](https://github.com/forwardemail/email-templates/actions/workflows/ci.yml/badge.svg)](https://github.com/forwardemail/email-templates/actions/workflows/ci.yml)\n[![code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)\n[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)\n[![made with lass](https://img.shields.io/badge/made_with-lass-95CC28.svg)](https://lass.js.org)\n[![license](https://img.shields.io/github/license/forwardemail/email-templates.svg)](LICENSE)\n\nCreate, [preview][preview-email] (browser/iOS Simulator), and send custom email templates for [Node.js][node].  Made for [Forward Email][forward-email] and [Lad][].\n\n\u003e **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)**\n\n\n## Table of Contents\n\n* [Install](#install)\n* [Preview](#preview)\n* [Usage](#usage)\n  * [Debugging](#debugging)\n  * [Basic](#basic)\n  * [Attachments](#attachments)\n  * [Automatic Inline CSS via Stylesheets](#automatic-inline-css-via-stylesheets)\n  * [Render HTML and/or Text](#render-html-andor-text)\n  * [Localization](#localization)\n  * [Text-Only Email (no HTML)](#text-only-email-no-html)\n  * [Prefix Subject Lines](#prefix-subject-lines)\n  * [Custom Text Template](#custom-text-template)\n  * [Custom Template Engine (e.g. EJS)](#custom-template-engine-eg-ejs)\n  * [Custom Default Message Options](#custom-default-message-options)\n  * [Custom Rendering (e.g. from a MongoDB database)](#custom-rendering-eg-from-a-mongodb-database)\n  * [Absolute Path to Templates](#absolute-path-to-templates)\n  * [Open Email Previews in Firefox](#open-email-previews-in-firefox)\n* [Options](#options)\n* [Tips](#tips)\n  * [Purge unused CSS](#purge-unused-css)\n  * [Optimized Pug Stylesheet Loading](#optimized-pug-stylesheet-loading)\n* [Plugins](#plugins)\n* [Breaking Changes](#breaking-changes)\n  * [v13.0.0](#v1300)\n  * [v12.0.0](#v1200)\n  * [v11.0.0](#v1100)\n  * [v10.0.0](#v1000)\n  * [v9.0.0](#v900)\n  * [v8.0.0](#v800)\n  * [v7.0.0](#v700)\n  * [v6.0.0](#v600)\n  * [v5.0.0](#v500)\n  * [v4.0.0](#v400)\n  * [v3.0.0](#v300)\n* [Related](#related)\n* [Contributors](#contributors)\n* [License](#license)\n\n\n## Install\n\n\u003e 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.\n\n[npm][]:\n\n```sh\nnpm install email-templates preview-email pug\n```\n\n\n## Preview\n\nWe've added [preview-email][] by default to this package.  This package allows you to preview emails in the browser and in the iOS Simulator.\n\nThis 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.\n\nIf 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' } }`).\n\nSee the example below for [Open Email Previews in Firefox](#open-email-previews-in-firefox).\n\n\n## Usage\n\n### Debugging\n\n#### Environment Flag\n\nIf you run into any issues with configuration, files, templates, locals, etc, then you can use the `NODE_DEBUG` environment flag:\n\n```sh\nNODE_DEBUG=email-templates node app.js\n```\n\nThis will output to the console all debug statements in our codebase for this package.\n\n#### Inspect Message\n\nAs of v3.6.1 you can now inspect the message passed to `nodemailer.sendMail` internally.\n\nIn the response object from `email.send`, you have access to `res.originalMessage`:\n\n```js\nemail\n  .send({\n    template: 'mars',\n    message: {\n      to: 'elon@spacex.com'\n    },\n    locals: {\n      name: 'Elon'\n    }\n  })\n  .then(res =\u003e {\n    console.log('res.originalMessage', res.originalMessage)\n  })\n  .catch(console.error);\n```\n\n### Basic\n\n\u003e 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][]).\n\u003e\n\u003e If you want to send emails in `development` or `test` environments, set `options.send` to `true`.\n\n```js\nconst Email = require('email-templates');\n\nconst email = new Email({\n  message: {\n    from: 'test@example.com'\n  },\n  // uncomment below to send emails in development/test env:\n  // send: true\n  transport: {\n    jsonTransport: true\n  }\n});\n\nemail\n  .send({\n    template: 'mars',\n    message: {\n      to: 'elon@spacex.com'\n    },\n    locals: {\n      name: 'Elon'\n    }\n  })\n  .then(console.log)\n  .catch(console.error);\n```\n\nThe example above assumes you have the following directory structure:\n\n```sh\n.\n├── app.js\n└── emails\n    └── mars\n        ├── html.pug\n        └── subject.pug\n```\n\nAnd the contents of the `pug` files are:\n\n\u003e `html.pug`:\n\n```pug\np Hi #{name},\np Welcome to Mars, the red planet.\n```\n\n\u003e `subject.pug`:\n\n```pug\n= `Hi ${name}, welcome to Mars`\n```\n\n### Attachments\n\nPlease reference [Nodemailer's attachment documentation][attachments] for further reference.\n\n\u003e If you want to set default attachments sent with every email:\n\n```js\nconst Email = require('email-templates');\n\nconst email = new Email({\n  message: {\n    from: 'test@example.com',\n    attachments: [\n      {\n        filename: 'text1.txt',\n        content: 'hello world!'\n      }\n    ]\n  }\n});\n\nemail\n  .send({\n    template: 'mars',\n    message: {\n      to: 'elon@spacex.com'\n    },\n    locals: {\n      name: 'Elon'\n    }\n  })\n  .then(console.log)\n  .catch(console.error);\n```\n\n\u003e If you want to set attachments sent individually:\n\n```js\nconst Email = require('email-templates');\n\nconst email = new Email({\n  message: {\n    from: 'test@example.com'\n  },\n  transport: {\n    jsonTransport: true\n  }\n});\n\nemail\n  .send({\n    template: 'mars',\n    message: {\n      to: 'elon@spacex.com',\n      attachments: [\n        {\n          filename: 'text1.txt',\n          content: 'hello world!'\n        }\n      ]\n    },\n    locals: {\n      name: 'Elon'\n    }\n  })\n  .then(console.log)\n  .catch(console.error);\n```\n\n### Automatic Inline CSS via Stylesheets\n\nSimply include the path or URL to the stylesheet in your template's `\u003chead\u003e`:\n\n```pug\nlink(rel=\"stylesheet\", href=\"/css/app.css\", data-inline)\n```\n\nThis will look for the file `/css/app.css` in the `build/` folder.  Also see [Optimized Pug Stylesheet Loading](#optimized-pug-stylesheet-loading) below.\n\nIf this asset is in another folder, then you will need to modify the default options when creating an `Email` instance:\n\n```js\nconst email = new Email({\n  // \u003chttps://github.com/Automattic/juice\u003e\n  juice: true,\n  // Override juice global settings \u003chttps://github.com/Automattic/juice#juicecodeblocks\u003e\n  juiceSettings: {\n    tableElements: ['TABLE']\n  },\n  juiceResources: {\n    // set this to `true` (since as of v11 it is `false` by default)\n    applyStyleTags: true, // \u003c------------ you need to set this to `true`\n    webResources: {\n      //\n      // this is the relative directory to your CSS/image assets\n      // and its default path is `build/`:\n      //\n      // e.g. if you have the following in the `\u003chead`\u003e of your template:\n      // `\u003clink rel=\"stylesheet\" href=\"style.css\" data-inline=\"data-inline\"\u003e`\n      // then this assumes that the file `build/style.css` exists\n      //\n      relativeTo: path.resolve('build')\n      //\n      // but you might want to change it to something like:\n      // relativeTo: path.join(__dirname, '..', 'assets')\n      // (so that you can re-use CSS/images that are used in your web-app)\n      //\n    }\n  }\n});\n```\n\n### Render HTML and/or Text\n\nIf you don't need this module to send your email, you can still use it to render HTML and/or text templates.\n\nSimply use the `email.render(view, locals)` method we expose (it's the same method that `email.send` uses internally).\n\n\u003e If you need to render a specific email template file (e.g. the HTML version):\n\n```js\nconst Email = require('email-templates');\n\nconst email = new Email();\n\nemail\n  .render('mars/html', {\n    name: 'Elon'\n  })\n  .then(console.log)\n  .catch(console.error);\n```\n\nThe example above assumes you have the following directory structure (note that this example would only render the `html.pug` file):\n\n```sh\n.\n├── app.js\n└── emails\n    └── mars\n        ├── html.pug\n        ├── text.pug\n        └── subject.pug\n```\n\nThe Promise for `email.render` resolves with a String (the HTML or text rendered).\n\n\u003e If you need pass juiceResources in render function, with this option you don't need create Email instance every time\n\n```js\nconst Email = require('email-templates');\n\nconst email = new Email();\n\nemail\n  .render({\n    path: 'mars/html',\n    juiceResources: {\n      webResources: {\n        // view folder path, it will get css from `mars/style.css`\n        relativeTo: path.resolve('mars')\n      }\n    }\n  }, {\n    name: 'Elon'\n  })\n  .then(console.log)\n  .catch(console.error);\n```\n\nThe 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\n\n```sh\n.\n├── app.js\n└── emails\n    └── mars\n        ├── html.pug\n        ├── text.pug\n        ├── subject.pug\n        └── style.css\n```\n\nThe Promise for `email.render` resolves with a String (the HTML or text rendered).\n\n\u003e 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).\n\n```js\nconst Email = require('email-templates');\n\nconst email = new Email();\n\nemail\n  .renderAll('mars', {\n    name: 'Elon'\n  })\n  .then(console.log)\n  .catch(console.error);\n```\n\n\u003e 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`:\n\n```js\nconst Email = require('email-templates');\n\nconst email = new Email();\nconst locals = { name: 'Elon' };\n\nPromise\n  .all([\n    email.render('mars/html', locals),\n    email.render('mars/text', locals)\n  ])\n  .then(([ html, text ]) =\u003e {\n    console.log('html', html);\n    console.log('text', text);\n  })\n  .catch(console.error);\n```\n\n### Localization\n\nAll 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).\n\n\u003e 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.\n\n```js\nconst Email = require('email-templates');\n\nconst email = new Email({\n  message: {\n    from: 'test@example.com'\n  },\n  transport: {\n    jsonTransport: true\n  },\n  i18n: {} // \u003c------ HERE\n});\n\nemail\n  .send({\n    template: 'mars',\n    message: {\n      to: 'elon@spacex.com'\n    },\n    locals: {\n      locale: 'en', // \u003c------ CUSTOMIZE LOCALE HERE (defaults to `i18n.defaultLocale` - `en`)\n      // is your user french?\n      // locale: 'fr',\n      name: 'Elon'\n    }\n  })\n  .then(console.log)\n  .catch(console.error);\n```\n\nThen slightly modify your templates to use localization functions.\n\n\u003e `html.pug`:\n\n```pug\np= `${t('Hi')} ${name},`\np= t('Welcome to Mars, the red planet.')\n```\n\n\u003e `subject.pug`:\n\n```pug\np= `${t('Hi')} ${name}, ${t('welcome to Mars')}`\n```\n\nNote that if you use [Lad][], you have a built-in filter called `translate`:\n\n```pug\np: :translate(locale) Welcome to Mars, the red planet.\n```\n\n#### Localization using Handlebars template engine\n\nIf you are using handlebars and you are using localization files with named values, you will quickly see that\nthere is no way to properly call the `t` function in your template and specify named values.\n\nIf, for example you have this in your translation file:\n\n```json\n{\n  \"greetings\": \"Hi {{ firstname }}\",\n  \"welcome_message\": \"Welcome to Mars, the red planet.\"\n}\n```\n\nAnd you would like to use it in your template like this:\n\n\u003e `html.hbs`:\n\n```handlebars\n\u003cp\u003e{{ t \"greetings\" firstname=\"Marcus\" }}\u003c/p\u003e\n\u003cp\u003e{{ t \"welcome_message\" }}\u003c/p\u003e\n```\n\nThis would not work because the second argument sent by handlebars to the function would be a handlebar helper\noptions object instead of just the named values.\n\nA possible workaround you can use is to introduce your own translation helper in your template locals:\n\n```js\nemail\n  .send({\n    template: 'mars',\n    message: {\n      to: 'elon@spacex.com'\n    },\n    locals: {\n      locale: 'en', // \u003c------ CUSTOMIZE LOCALE HERE (defaults to `i18n.defaultLocale` - `en`)\n      // is your user french?\n      // locale: 'fr',\n      name: 'Elon',\n      $t(key, options) {\n        // \u003c------ THIS IS OUR OWN TRANSLATION HELPER\n        return options.data.root.t(\n          { phrase: key, locale: options.data.root.locale },\n          options.hash\n        );\n      }\n    }\n  })\n  .then(console.log)\n  .catch(console.error);\n```\n\nThen slightly modify your templates to use your own translation helper functions.\n\n\u003e `html.hbs`:\n\n```handlebars\n\u003cp\u003e{{ $t \"greetings\" firstname=\"Marcus\" }}\u003c/p\u003e\n\u003cp\u003e{{ $t \"welcome_message\" }}\u003c/p\u003e\n```\n\n### Text-Only Email (no HTML)\n\nIf you wish to have only a text-based version of your email you can simply pass the option `textOnly: true`.\n\nRegardless if you use the `htmlToText` option or not (see next example), it will still render only a text-based version.\n\n```js\nconst Email = require('email-templates');\n\nconst email = new Email({\n  message: {\n    from: 'test@example.com'\n  },\n  transport: {\n    jsonTransport: true\n  },\n  textOnly: true // \u003c----- HERE\n});\n\nemail\n  .send({\n    template: 'mars',\n    message: {\n      to: 'elon@spacex.com'\n    },\n    locals: {\n      name: 'Elon'\n    }\n  })\n  .then(console.log)\n  .catch(console.error);\n```\n\n### Prefix Subject Lines\n\nYou can pass an option to prefix subject lines with a string, which is super useful for deciphering development / staging / production environment emails.\n\nFor example, you could make it so on non-production environments the email is prefixed with a `[DEVELOPMENT] Some Subject Line Here`.\n\nYou 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.\n\nSimply 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**):\n\n```js\nconst Email = require('email-templates');\n\nconst env = process.env.NODE_ENV || 'development';\n\nconst email = new Email({\n  message: {\n    from: 'test@example.com'\n  },\n  transport: {\n    jsonTransport: true\n  },\n  subjectPrefix: env === 'production' ? false : `[${env.toUpperCase()}] `; // \u003c--- HERE\n});\n```\n\n### Custom Text Template\n\n\u003e By default we use `html-to-text` to generate a plaintext version and attach it as `message.text`.\n\nIf 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`.\n\nYou may also set `config.htmlToText: false` to force the usage of the `text` template file.\n\n```js\nconst Email = require('email-templates');\n\nconst email = new Email({\n  message: {\n    from: 'test@example.com'\n  },\n  transport: {\n    jsonTransport: true\n  },\n  htmlToText: false // \u003c----- HERE\n});\n\nemail\n  .send({\n    template: 'mars',\n    message: {\n      to: 'elon@spacex.com'\n    },\n    locals: {\n      name: 'Elon'\n    }\n  })\n  .then(console.log)\n  .catch(console.error);\n```\n\n\u003e `text.pug`:\n\n```pug\n| Hi #{name},\n| Welcome to Mars, the red planet.\n```\n\n### Custom Template Engine (e.g. EJS)\n\n1. Install your desired template engine (e.g. [EJS][])\n\n   [npm][]:\n\n   ```sh\n   npm install ejs\n   ```\n\n2. Set the extension in options and send an email\n\n   ```js\n   const Email = require('email-templates');\n\n   const email = new Email({\n     message: {\n       from: 'test@example.com'\n     },\n     transport: {\n       jsonTransport: true\n     },\n     views: {\n       options: {\n         extension: 'ejs' // \u003c---- HERE\n       }\n     }\n   });\n   ```\n\n### Custom Default Message Options\n\nYou can configure your Email instance to have default message options, such as a default \"From\", an unsubscribe header, etc.\n\nFor a list of all available message options and fields see [the Nodemailer message reference](https://nodemailer.com/message/).\n\n\u003e Here's an example showing how to set a default custom header and a list unsubscribe header:\n\n```js\nconst Email = require('email-templates');\n\nconst email = new Email({\n  message: {\n    from: 'test@example.com',\n    headers: {\n      'X-Some-Custom-Thing': 'Some-Value'\n    },\n    list: {\n      unsubscribe: 'https://example.com/unsubscribe'\n    }\n  },\n  transport: {\n    jsonTransport: true\n  }\n});\n```\n\n### Custom Rendering (e.g. from a MongoDB database)\n\nYou can pass a custom `config.render` function which accepts two arguments `view` and `locals` and must return a `Promise`.\n\nNote 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.\n\nIf you wanted to read a stored EJS template from MongoDB, you could do something like:\n\n```js\nconst ejs = require('ejs');\n\nconst email = new Email({\n  // ...\n  render: (view, locals) =\u003e {\n    return new Promise((resolve, reject) =\u003e {\n      // this example assumes that `template` returned\n      // is an ejs-based template string\n      // view = `${template}/html` or `${template}/subject` or `${template}/text`\n      db.templates.findOne({ name: view }, (err, template) =\u003e {\n        if (err) return reject(err);\n        if (!template) return reject(new Error('Template not found'));\n        let html = ejs.render(template, locals);\n        html = await email.juiceResources(html);\n        resolve(html);\n      });\n    });\n  }\n});\n```\n\n### Absolute Path to Templates\n\nAs 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).\n\nFor both `email.send` and `email.render`, the `template` option passed can be a relative path or absolute:\n\n\u003e Relative example:\n\n```js\nemail\n  .send({\n    template: 'mars',\n    message: {\n      to: 'elon@spacex.com'\n    },\n    locals: {\n      name: 'Elon'\n    }\n  })\n  .then(console.log)\n  .catch(console.error);\n```\n\n\u003e Absolute example:\n\n```js\nconst path = require('path');\n\n// ...\n\nemail\n  .send({\n    template: path.join(__dirname, 'some', 'folder', 'mars')\n    message: {\n      to: 'elon@spacex.com'\n    },\n    locals: {\n      name: 'Elon'\n    }\n  })\n  .then(console.log)\n  .catch(console.error);\n```\n\n### Open Email Previews in Firefox\n\nThe `preview` option can be a custom Object of options to pass along to [open's options][open-options].\n\n\u003e Firefox example:\n\n```js\nconst email = new Email({\n  // ...\n  preview: {\n    open: {\n      app: 'firefox',\n      wait: false\n    }\n  }\n});\n```\n\n\n## Options\n\nFor a list of all available options and defaults [view the configuration object](src/index.js), or reference the list below:\n\n* `views` (Object)\n  * `root` (String) - defaults to the current working directory's \"emails\" folder via `path.resolve('emails')`\n  * `options` (Object)\n    * `extension` (String) - defaults to `'pug'`, and is the default file extension for templates\n    * `map` (Object) - a template file extension mapping, defaults to `{ hbs: 'handlebars', njk: 'nunjucks' }` (this is useful if you use different file extension naming conventions)\n    * `engineSource` (Object) - the default template engine source, defaults to [@ladjs/consolidate][consolidate]\n  * `locals` (Object) - locals to pass to templates for rendering\n    * `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\n    * `pretty` (Boolean) - defaults to `true`, but is automatically set to `false` for subject templates and text-based emails\n* `message` (Object) - default [Nodemailer message object][nodemailer-message-object] for messages to inherit (defaults to an empty object `{}`)\n* `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`**)\n* `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 }`\n* `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\n* `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))\n* `customRender` (Boolean) - defaults to `false`, unless you pass your own `render` function, and in that case it will be automatically set to `true`\n* `textOnly` (Boolean) - whether or not to force text-only rendering of a template and disregard the template folder (defaults to `false`)\n* `htmlToText` (Object) - configuration object for [html-to-text][]\n  * `ignoreImage` (Boolean) - defaults to `true`\n* `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\n* `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)\n* `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\n  * `applyStyleTags` (Boolean) - defaults to `false` (as of v11, since modern browsers now support `\u003cstyle\u003e` tag in `\u003chead\u003e` section)\n  * `removeStyleTags` (Boolean) - defaults to `false` (as of v11, since modern browsers now support `\u003cstyle\u003e` tag in `\u003chead\u003e` section)\n  * `webResources` (Object) - an options object that will be passed to [web-resource-inliner][]\n    * `relativeTo` (String) - defaults to the current working directory's \"build\" folder via `path.resolve('build')` (**NOTE: YOU SHOULD MODIFY THIS PATH TO WHERE YOUR BUILD/ASSETS FOLDER IS**)\n    * `images` (Boolean or Number) - defaults to `false`, and is  whether or not to inline images unless they have an exclusion attribute (see [web-resource-inliner][] for more insight), if it is set to a Number then that is used as the KB threshold\n* `transport` (Object) - a transport configuration object or a Nodemailer transport instance created via `nodemailer.createTransport`, defaults to an empty object `{}`, see [Nodemailer transports][nodemailer-transports] documentation for more insight\n* `getPath` (Function) - a function that returns the path to a template file, defaults to `function (type, template) { return path.join(template, type); }`, and accepts three arguments `type`, `template`, and `locals`\n\n\n## Tips\n\n### Purge unused CSS\n\nSee the `gulpfile.js` file and `email` directory in the [Forward Email][forward-email-code] codebase for insight as to how to use `purge-css` across your email templates.\n\n### Optimized Pug Stylesheet Loading\n\nNote that if you're using [pug][], you can use the following pattern to optimize compile time for rendering in your email layout.\n\n```diff\ndoctype html\nhtml\n  head\n-    link(rel='stylesheet', href='style.css', data-inline)\n+    style\n+      include style.css\n  body\n    p Hello\n```\n\nThis makes use of pug [includes](https://pugjs.org/language/includes.html) – which saves compilation time for rendering (since [web-resource-inliner][] will not have to fetch the external stylesheet).\n\n\n## Plugins\n\nYou can use any [nodemailer][] plugin. Simply pass an existing transport instance as `config.transport`.\n\nYou should add the [nodemailer-base64-to-s3][] plugin to convert base64 inline images to actual images stored on Amazon S3 and Cloudfront.\n\nWhen doing so (as of v4.0.2+), you will need to adjust your `email-templates` configuration to pass `images: true` as such:\n\n```js\nconst email = new Email({\n  // ...\n  juiceResources: {\n    webResources: {\n      relativeTo: path.resolve('build'),\n      images: true // \u003c--- set this as `true`\n    }\n  }\n});\n```\n\n\n## Breaking Changes\n\nSee the [Releases](https://github.com/forwardemail/email-templates/releases) page for an up to date changelog.\n\n### v13.0.0\n\nThis package now requires Node v18+.\n\n### v12.0.0\n\nThe `preview-email` dependency is now an optional dependency.  You will need to `npm install preview-email` or set `preview: false`, otherwise an error will be thrown in non-production environments and `console.error` in production environments if `preview` option is a truthy value.  The default value for `preview` is `preview: process.NODE_ENV === 'development'`.\n\n### v11.0.0\n\nThis package no longer inlines stylesheets by default and preserves `\u003cstyle\u003e` tags in the `\u003chead\u003e` (see [Options](#options)).\n\nA majority of email clients support `\u003cstyle\u003e` tags in the `\u003chead\u003e` section – and inlining CSS is no longer necessary.\n\nSee [1](https://www.caniemail.com/features/html-style/), [2](https://www.campaignmonitor.com/css/style-element/style-in-head/), and [3](https://caniuse.email/) as references.\n\n### v10.0.0\n\nThis package now requires Node v14+.\n\n### v9.0.0\n\nThis package now requires Node v10.x+ due to [web-resource-inliner](https://github.com/jrit/web-resource-inliner/blob/master/HISTORY.md#2020-07-09-v500) dependency.\n\n### v8.0.0\n\nWe upgraded [html-to-text](https://github.com/html-to-text/node-html-to-text) to v6. As a result, automatically generated text versions of your emails will look slightly different, as per the example below:\n\n```diff\n+Hi,\n+\n+email-templates rocks!\n+\n+Cheers,\n+The team\n-Hi,email-templates rocks!\n-Cheers,The team\n```\n\n### v7.0.0\n\nWe upgraded `preview-email` to `v2.0.0`, which supports stream attachments, and additionally the view rendering is slightly different (we simply iterate over header lines and format them in a `\u003cpre\u003e\u003ccode\u003e` block).  A major version bump was done due to the significant visual change in the preview rendering of emails.\n\n### v6.0.0\n\n* Performance should be significantly improved as the rendering of subject, html, and text parts now occurs asynchronously in parallel (previously it was in series and had blocking lookup calls).\n* We removed [bluebird][] and replaced it with a lightweight alternative [pify][] (since all we were using was the `Promise.promisify` method from `bluebird` as well).\n* This package now only supports Node v8.x+ (due to [preview-email][]'s [open][] dependency requiring it).\n* Configuration for the `preview` option has slightly changed, which now allows you to [specify a custom template and stylesheets](https://github.com/forwardemail/preview-email#custom-preview-template-and-stylesheets) for preview rendering.\n\n  \u003e If you were using a custom `preview` option before, you will need to change it slightly:\n\n  ```diff\n  const email = new Email({\n    // ...\n    preview: {\n  +    open: {\n  +      app: 'firefox',\n  +      wait: false\n  +    }\n  -    app: 'firefox',\n  -    wait: false\n    }\n  });\n  ```\n\n### v5.0.0\n\nIn version 4.x+, we changed the order of defaults being set.  See [#313](https://github.com/forwardemail/email-templates/issues/313) for more information.  This allows you to override message options such as `from` (even if you have a global default `from` set).\n\n### v4.0.0\n\nSee v5.0.0 above\n\n### v3.0.0\n\n\u003e If you are upgrading from v2 or prior to v3, please note that the following breaking API changes occurred:\n\n1. You need to have Node v6.4.0+, we recommend using [nvm](https://github.com/creationix/nvm) to manage your Node versions.\n\n2. Instead of calling `const newsletter = new EmailTemplate(...args)`, you now call `const email = new Email(options)`.\n\n   * The arguments you pass to the constructor have changed as well.\n   * 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.\n   * If your `templateDir` path is the \"emails\" folder in the root of your project (basically `./emails` folder) then you do not need to pass it at all since it is the default per the [configuration object](src/index.js).\n   * The previous value for `templateDir` can be used as such:\n\n   ```diff\n   -const newsletter = new EmailTemplate(templateDir);\n   +const email = new Email({\n   +  views: { root: templateDir }\n   +});\n   ```\n\n   * Note that if you are inlining CSS, you should also make sure that the option for `juiceResources.webResources.relativeTo` is accurate.\n\n3. Instead of calling `newsletter.render(locals, callback)` you now call `email.render(template, locals)`.  The return value of `email.render` when invoked is a `Promise` and does not accept a callback function.\n\n   \u003e **NOTE**: `email-templates` v3 now has an `email.send` method ([see basic usage example](#basic)) which uses `nodemailer`; you should now use `email.send` instead of `email.render`!\n\n   ```diff\n   -newsletter.render({}, (err, result) =\u003e {\n   -  if (err) return console.error(err);\n   -  console.log(result);\n   -});\n   +email.render(template, {}).then(console.log).catch(console.error);\n   ```\n\n4. Localized template directories are no longer supported.  We now support i18n translations out of the box.  See [Localization](#localization) for more info.\n\n5. A new method `email.send` has been added.  This allows you to create a Nodemailer transport and send an email template all at once (it calls `email.render` internally).  See the [Basic](#basic) usage documentation above for an example.\n\n6. There are new options `options.send` and `options.preview`.  Both are Boolean values and configured automatically based off the environment.  Take a look at the [configuration object](src/index.js).  Note that you can optionally pass an Object to `preview` option, which gets passed along to [open's options][open-options].\n\n7. If you wish to send emails in development or test environment (disabled by default), set `options.send` to `true`.\n\n\n## Related\n\n* [forward-email][] - Free, encrypted, and open-source email forwarding service for custom domains\n* [custom-fonts-in-emails][] - render any font in emails as an image w/retina support (no more Photoshop or Sketch exports)\n* [font-awesome-assets][] - render any [Font Awesome][fa] icon as an image in an email w/retina support (no more Photoshop or Sketch exports!)\n* [lad][] - Scaffold a [Koa][] webapp and API framework for [Node.js][node]\n* [lass][] - Scaffold a modern boilerplate for [Node.js][node]\n* [cabin][] - Logging and analytics solution for [Node.js][node], [Lad][], [Koa][], and [Express][]\n\n\n## Contributors\n\n| Name           | Website                   |\n| -------------- | ------------------------- |\n| **Nick Baugh** | \u003chttp://niftylettuce.com\u003e |\n\n\n## License\n\n[MIT](LICENSE) © [Nick Baugh](http://niftylettuce.com)\n\n\n##\n\n[node]: https://nodejs.org\n\n[npm]: https://www.npmjs.com/\n\n[pug]: https://pugjs.org\n\n[supported-engines]: https://github.com/ladjs/consolidate/#engines\n\n[nodemailer]: https://nodemailer.com/plugins/\n\n[font-awesome-assets]: https://github.com/ladjs/font-awesome-assets\n\n[custom-fonts-in-emails]: https://github.com/ladjs/custom-fonts-in-emails\n\n[nodemailer-base64-to-s3]: https://github.com/ladjs/nodemailer-base64-to-s3\n\n[lad]: https://lad.js.org\n\n[i18n]: https://github.com/ladjs/i18n#options\n\n[fa]: http://fontawesome.io/\n\n[nodemailer-transport]: https://nodemailer.com/transports/\n\n[ejs]: http://ejs.co/\n\n[preview-email]: https://github.com/forwardemail/preview-email\n\n[attachments]: https://nodemailer.com/message/attachments/\n\n[lass]: https://lass.js.org\n\n[cabin]: https://cabinjs.com\n\n[forward-email]: https://forwardemail.net\n\n[koa]: https://koajs.com/\n\n[express]: https://expressjs.com\n\n[open-options]: https://github.com/sindresorhus/open#options\n\n[mandarin]: https://github.com/ladjs/mandarin\n\n[consolidate]: https://github.com/ladjs/consolidate\n\n[nodemailer-message-object]: https://nodemailer.com/message/\n\n[html-to-text]: https://github.com/werk85/node-html-to-text\n\n[web-resource-inliner]: https://github.com/jrit/web-resource-inliner\n\n[nodemailer-transports]: https://nodemailer.com/transports/\n\n[juice]: https://github.com/Automattic/juice\n\n[bluebird]: https://github.com/petkaantonov/bluebird\n\n[pify]: https://github.com/sindresorhus/pify\n\n[open]: https://github.com/sindresorhus/open\n\n[forward-email-code]: https://github.com/forwardemail/forwardemail.net\n","funding_links":[],"categories":["Packages","JavaScript","包","Repository","Github Repositories","Uncategorized"],"sub_categories":["Email","邮件","Frontend Development","Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fforwardemail%2Femail-templates","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fforwardemail%2Femail-templates","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fforwardemail%2Femail-templates/lists"}