How to implement HTML email templates using Express.js with Mailgun and Handlebars

I’m trying to build an email system for my web application using Express.js and Mailgun service. I want to send HTML emails with dynamic content using Handlebars templates, but I keep running into issues.

Here’s my current setup:

const mail = require('nodemailer');
const handlebars = require('nodemailer-express-handlebars');
const mailgunTransport = require('nodemailer-mailgun-transport');

const hbsConfig = {
  viewEngine: {
    extname: '.hbs',
    layoutsDir: 'templates/mail/',
    defaultLayout: 'main',
    partialsDir: 'templates/components/'
  },
  viewPath: 'templates/mail/',
  extName: '.hbs'
};

const credentials = {
  auth: {
    api_key: 'your-mailgun-key',
    domain: 'your-domain.com'
  }
};

const transport = mail.createTransport(mailgunTransport(credentials));
transport.use('compile', handlebars(hbsConfig));

transport.sendMail({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Welcome Message',
  template: 'welcome',
  context: {
    userName: 'John',
    userEmail: '[email protected]'
  }
}, (err, info) => {
  if (err) {
    console.error('Send failed:', err);
  } else {
    console.log('Email sent:', info);
  }
});

My template files:

templates/mail/main.hbs

{{>mail/head}}
<body>
  {{>mail/nav}}
  {{{body}}}
  {{>mail/bottom}}
</body>
</html>

templates/mail/welcome.hbs

<h2>Welcome to our platform!</h2>
<p>Hello {{userName}}</p>
<p>Your email: {{userEmail}}</p>

But I get this error message:

Error: Sorry: template parameter is not supported yet. Check back soon!

I need to figure out how to properly send templated emails through Mailgun. The same code works fine with other email providers. Has anyone successfully implemented this setup? What’s the correct approach for using templates with Mailgun transport?

The nodemailer-mailgun-transport doesn’t support that template parameter you’re trying to use. I encountered this issue last year—Mailgun’s transport just doesn’t handle template compilation like SMTP transports do. What worked for me was to manually compile the Handlebars template first. Use fs.readFileSync to retrieve your template file, then apply handlebars.compile() with your data. After that, pass the HTML string directly to the html property in sendMail instead of dealing with the template and context. This gives you better control over rendering and avoids Mailgun’s limitations altogether.

Yes, nodemailer-mailgun-transport doesn’t work seamlessly with Handlebars templates as typical SMTP transports do. I faced a similar issue six months ago when transitioning from SendGrid to Mailgun. The solution is to manually compile your templates before sending them. Avoid using the template property altogether; instead, utilize handlebars.compile() to create a template function, which you can then invoke with your data to generate the final HTML string. Insert this compiled HTML directly into the html property, bypassing the need for template and context, effectively addressing Mailgun’s limitations with templates while still allowing for dynamic content.

Honestly, skip nodemailer and go straight to Mailgun’s API - it’s way easier. Use handlebars.compile() to render your HTML first, then send it through Mailgun’s SDK. Just do const html = template(context) and pass that HTML to Mailgun. You’ll avoid all the transport setup hassles.