email-templates: rendering and sending Pug, EJS and other Node.js email templates
Create, preview (browser/iOS Simulator), and send custom email templates for Node.js. Made for @forwardemail, @ladjs, @cabinjs, @spamscanner, and @breejs.
At a glance
- What is it?
- email-templates is a Node.js package that renders template files into HTML and text emails, inlines stylesheets, and hands the result to Nodemailer. It suits Node developers who keep email markup in version control rather than a hosted editor.
- Who is it for?
- Adopt email-templates if your transactional email already lives in a Node service and you want the markup in the repository, rendered with Pug, EJS or another supported engine, and inlined before it reaches Nodemailer. Do not adopt it if you need a visual editor, a drag-and-drop builder, or a hosted template service that non-developers can edit, because this package has no such interface and its preview mode writes to a temporary directory.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 33 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What email-templates does that a mail transport does not
Nodemailer sends a message. It does not decide what the message contains. email-templates sits in front of it and turns a directory of template files into the html and text parts of a message, resolves the subject line from its own template, and then calls into the transport. The README describes the package as a way to create, preview and send custom email templates for Node.js, and the dependency list confirms the shape of that job: nodemailer for delivery, juice for CSS inlining, html-to-text for the plain-text alternative, @ladjs/consolidate for template engine support, and @ladjs/i18n for localization.
The intended audience is a Node developer working inside a service that already sends transactional mail. The README says the package is made for Forward Email and Lad, and the examples use a template named mars with a locals object containing a name. If your email is authored by a marketing person in a web editor, this package is not that. There is no editor, no database of templates and no admin UI in the repository layout. Templates are files on disk, and the repository is the source of truth.
How a template directory becomes a sent message
The unit of work is a directory under emails, named after the template. The README's basic example assumes this layout: an app.js at the root and an emails/mars directory containing html.pug and subject.pug. Both files are rendered with the same locals, so a single name variable can appear in the body and in the subject. The subject template in the example is a single expression: = `Hi ${name}, welcome to Mars`.
When you call send, the package resolves the template directory, renders each recognized file through the configured engine, inlines stylesheets that are marked for inlining, generates a text version through html-to-text, and passes the assembled message to nodemailer.sendMail. The response object exposes res.originalMessage, which the README documents as the message that was passed to sendMail internally. That is the practical debugging hook: when the rendered output does not match what you expected, originalMessage shows what the transport actually received.
CSS handling is explicit rather than automatic. A stylesheet is inlined only when the link tag carries the data-inline attribute, as in link(rel="stylesheet", href="/css/app.css", data-inline). The README states that this path is resolved inside the build folder, and that a different location requires changing the default options when constructing the Email instance. That resolution rule is the most common source of surprise: a stylesheet that exists at the path you wrote, but not under build, will not be found.
Installing email-templates and sending a first template
The README recommends Pug as the template engine and notes that preview-email is an optional dependency that renders development previews in the browser. The install command brings in all three:
npm install email-templates preview-email pugThe engines field in package.json requires Node 18 or later, so check that before anything else. The README's basic example assumes this directory structure, with html.pug and subject.pug inside emails/mars:
.
├── app.js
└── emails
└── mars
├── html.pug
└── subject.pugThe README gives the contents of those two files, starting with html.pug:
p Hi #{name},
p Welcome to Mars, the red planet.And subject.pug:
= `Hi ${name}, welcome to Mars`Now construct an Email instance and send. The README uses jsonTransport in this example, which serializes the message instead of delivering it, and leaves send commented out with the note that you must set send to true to send in development or test environments:
const Email = require('email-templates');
const email = new Email({
message: {
from: '[email protected]'
},
// uncomment below to send emails in development/test env:
// send: true
transport: {
jsonTransport: true
}
});The send call takes the template name, the message fields and the locals:
email
.send({
template: 'mars',
message: {
to: '[email protected]'
},
locals: {
name: 'Elon'
}
})
.then(console.log)
.catch(console.error);With NODE_ENV=development, the README states that emails are rendered to the tmp directory and opened in the browser automatically. If the browser does not open, the preview option is passed through to open's options; the README gives preview: { open: { app: 'firefox' } } as the Firefox example. For configuration problems, the README documents the NODE_DEBUG flag:
NODE_DEBUG=email-templates node app.jsThat prints the package's own debug statements, which is the fastest way to see which template path or locals value the renderer actually resolved.
Where email-templates stops being the right tool
The package renders and sends. It does not schedule, retry, queue, or track delivery. If you need a campaign with an audience list, send-time optimization or open tracking, this is the wrong layer, and the README points readers to Forward Email for transport advice rather than claiming to solve deliverability itself.
The preview story is also narrower than it first appears. Preview output goes to a temporary directory and opens in a browser, which is fine for a developer on a laptop and awkward in CI, in a container without a browser, or for anyone who is not running Node locally. There is no built-in way for a designer to open a template without running the project.
Template engine support comes through @ladjs/consolidate, so the set of engines is whatever that package supports, not whatever you happen to have installed. Choosing an engine outside that set means the renderer will not find it. Localization goes through @ladjs/i18n, which is a real dependency and a real configuration surface, not a one-line toggle; teams that need per-recipient locale should read that package's documentation before assuming the behavior.
The release history shows a pattern worth noting. The major versions are frequent: v13.0.0 landed on 2025-12-31, v12.0.0 before it, and the README carries a Breaking Changes section going back to v3.0.0. That section exists because upgrades have repeatedly required code changes. Treat a major bump as a migration task with a changelog read, not a version range edit.
email-templates compared with MJML and hosted template services
MJML attacks the same problem from the markup side. It defines its own component language and compiles it into table-based HTML that survives Outlook, which is a different bet: you learn MJML tags, and the compiler owns the HTML. email-templates makes no such guarantee. It renders whatever Pug, EJS or another supported engine produces and inlines the CSS you point it at, so the HTML quality is your responsibility. If your team already writes email HTML by hand and wants it inlined and sent, email-templates fits. If nobody on the team wants to reason about Outlook's rendering quirks, a compiler that emits conservative markup is doing more work for you.
Hosted template services take the third position: the template lives in their system, edited in their UI, and rendered by their API. The trade is control for convenience. With email-templates the template is a file in your repository, reviewed in a pull request, and versioned with the code that sends it. That is the whole argument for it. It is also the whole cost, because a copy change becomes a deploy.
Licence, maintenance and what an upgrade costs
The package is MIT licensed, and the LICENSE file is at the repository root. MIT is permissive: it allows commercial use and modification with the copyright notice retained. This is a description of the licence text, not legal advice, and the usual caveat applies that your organization's own review decides whether it fits.
The repository is not archived, and the last push was on 2026-08-27. The most recent release listed is v13.0.1 from 2025-12-31, with v13.0.0 earlier the same day and v12.0.3 on 2025-06-05. So the codebase is moving between releases rather than sitting still, and the gap between the last release and the last push suggests changes accumulate on master before a version is cut.
The upgrade cost is concentrated in the Breaking Changes section. Because the README documents breaking changes for every major version from v3.0.0 through v13.0.0, the honest estimate is that a major bump is not free. The dependencies are the second cost centre: juice for inlining, html-to-text for the text part, nodemailer for delivery and @ladjs/consolidate for engines all move independently, and a rendering difference after an upgrade will usually surface in the generated HTML rather than in an exception. Keeping a rendered fixture of one or two templates under test is the cheapest way to notice that.
Editorial conclusion
Adopt email-templates if your transactional email already lives in a Node service and you want the markup in the repository, rendered with Pug, EJS or another supported engine, and inlined before it reaches Nodemailer. Do not adopt it if you need a visual editor, a drag-and-drop builder, or a hosted template service that non-developers can edit, because this package has no such interface and its preview mode writes to a temporary directory. Before committing, verify that your template engine of choice is supported, that your Node version satisfies the engines field of at least 18, and that you have set send to true if you expect delivery in a development or test environment, since the README states that emails are not sent there by default.
Frequently asked questions
How do I install email-templates?
Run npm install email-templates preview-email pug. The README recommends Pug as the template engine, and preview-email is an optional dependency that renders development previews in the browser. Node 18 or later is required by the engines field in package.json.
Why are my emails not being sent in development?
The README states that emails are not sent in development or test environments unless you set the send option to true. The basic example leaves that line commented out with a note explaining it.
Where does email-templates look for my inlined stylesheet?
A stylesheet is inlined only when the link tag has the data-inline attribute, and the README states the path is resolved inside the build folder. If the asset lives elsewhere, you must change the default options when creating the Email instance.
How can I see what message was actually passed to Nodemailer?
The response object from email.send exposes res.originalMessage, which the README documents as the message passed to nodemailer.sendMail internally. Logging it shows the rendered output the transport received.
Can I use a template engine other than Pug with email-templates?
Yes. The README links to a list of supported engines and notes that engine support comes through @ladjs/consolidate, so the available engines are the ones that package supports. The README's custom template engine example uses EJS.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/forwardemail-email-templates)