Mjml: Is it combinable with node-email-templates ?

Created on 24 Jan 2017  路  4Comments  路  Source: mjmlio/mjml

I am using https://github.com/crocodilejs/node-email-templates

Which is rather good to send emails from a node.js backend
because :

  • It allow to use our own template engine (pug in my case)
  • It allow to inline css
  • It allow of course to pass variables in emails (this passing variable is not clear in your doc by the way)

I have been trying to integrate it with mjml but I do not know if it is feasible.

To date my workflow is :

  • I write a pug file with mjml element name
  • This file get compiled in html with mjml tags
  • then compiled in the final html

I do not know how the inline styling succeed in node-email-template, but as the element name does not match as the final html is without any mjml component name.
I suppose it will fail doing its inlining jov.

So I do not see a workflow combining mjml and node-email-template
https://github.com/crocodilejs/node-email-templates

Currently the best seem to use a email template from theme forest or other open source separating content from structure thanks to jade.

But MJML though interesting does not seem to bring much.

Most helpful comment

Ok it works
I passed my html resulting for pug compilation
in

const mjml = require('mjml');
template.render(templateVars, function(err, renderedTemplate) {
      if (err) {
        debug('there was an error rendering the template.');
        return console.error(err)
      }
      debug(renderedTemplate.html);

renderedTemplate.html is looking like that

<mjml><mj-body><mj-section><mj-column width="100%"><mj-image width="100" src="http://res.cloudinary.com/hrscywv4p/image/upload/c_limit,h_1440,w_720,f_auto,q_90/v1/206926/LOGO_BAM_knz1yz.png"></mj-image><mj-divider border-color="#F45E43"></mj-divider><mj-text font-size="20px" color="#F45E43" font-family="helvetica">Bam Architecture</mj-text></mj-column></mj-section><mj-section><mj-text font-size="20px" color="#F45E43" font-family="helvetica"></mj-text></mj-section><div>Hello Sebastien Lucas</div><div>BAM vous propose une offre !</div><div>Dites-nous sous 24 heures si vous participer.</div><div class="call-action"><span>Voir le</span><a href="http://dev.bam.archi/offer/22?archiSelect">d茅tail de l'offre</a></div></mj-body></mjml>

then passed it to mjml.mjml2html function

      let mjmlRendered = mjml.mjml2html(renderedTemplate.html);

and get like this

{ errors: [],
  html: '<!doctype html>\n<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">\n<head>\n  <title></title>\n  <!--[if !mso]><!-- -->\n  <meta http-equiv="X-UA-Compatible" content="IE=edge">\n  <!--<![endif]-->\n<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\n<style type="text/css">\n  #outlook a { padding: 0; }\n  .ReadMsgBody { width: 100%; }\n  .ExternalClass { width: 100%; }\n  .ExternalClass * { line-height:100%; }\n  body { margin: 0; padding: 0; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }\n  table, td { border-collapse:collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; }\n  img { border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic; }\n  p { display: block; margin: 13px 0; }\n</style>\n<!--[if !mso]><!-->\n<style type="text/css">\n  @media only screen and (max-width:480px) {\n    @-ms-viewport { width:320px; }\n    @viewport { width:320px; }\n  }\n</style>\n<!--<![endif]-->\n<!--[if mso]>\n<xml>\n  <o:OfficeDocumentSettings>\n    <o:AllowPNG/>\n    <o:PixelsPerInch>96</o:PixelsPerInch>\n  </o:OfficeDocumentSettings>\n</xml>\n<![endif]-->\n<!--[if lte mso 11]>\n<style type="text/css">\n  .outlook-group-fix {\n    width:100% !important;\n  }\n</style>\n<![endif]-->\n<style type="text/css">\n  @media only screen and (min-width:480px) {\n    .mj-column-per-100 { width:100%!important; }\n  }\n</style>\n</head>\n<body>\n  <div style="margin:0px auto;"><table role="presentation" cellpadding="0" cellspacing="0" style="font-size:0px;width:100%;" align="center" border="0"><tbody><tr><td style="text-align:center;vertical-align:top;direction:ltr;font-size:0px;padding:20px 0px;"><!--[if mso | IE]>\n      <table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;width:NaNpx;">\n      <![endif]--><div class="mj-column-per-100 outlook-group-fix" style="vertical-align:top;display:inline-block;direction:ltr;font-size:13px;text-align:left;width:100%;"><table role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0"><tbody><tr><td style="word-break:break-word;font-size:0px;padding:10px 25px;" align="center"><table role="presentation" cellpadding="0" cellspacing="0" style="border-collapse:collapse;border-spacing:0px;" align="center" border="0"><tbody><tr><td style="width:50px;"><img alt="" title="" height="auto" src="http://res.cloudinary.com/hrscywv4p/image/upload/c_limit,h_1440,w_720,f_auto,q_90/v1/206926/LOGO_BAM_knz1yz.png" style="border:none;border-radius:;display:block;outline:none;text-decoration:none;width:100%;height:auto;" width="50"></td></tr></tbody></table></td></tr><tr><td style="word-break:break-word;font-size:0px;padding:10px 25px;"><p style="font-size:1px;margin:0px auto;border-top:4px solid #F45E43;width:100%;"></p><!--[if mso | IE]><table role="presentation" align="center" border="0" cellpadding="0" cellspacing="0" style="font-size:1px;margin:0px auto;border-top:4px solid #F45E43;width:100%;" width="100"><tr><td style="height:0;line-height:0;">聽</td></tr></table><![endif]--></td></tr><tr><td style="word-break:break-word;font-size:0px;padding:10px 25px;" align="left"><div class="" style="cursor:auto;color:#F45E43;font-family:helvetica;font-size:20px;line-height:22px;text-align:left;">Bam Architecture</div></td></tr></tbody></table></div><!--[if mso | IE]>\n      </td></tr></table>\n      <![endif]--></td></tr></tbody></table></div>\n</body>\n</html>' }

and I coud pass to node mailer the final version

transport.sendMail({

        from: emailSettings.from,

        to: email.to,
        subject: email.subject,

        bcc: emailSettings.bcc,

        html: mjmlRendered.html,
        text: renderedTemplate.text
      }

All 4 comments

Hi @sinsunsan

I don't know much about node-email-templates but it looks like a "full stack" css/sass/less + templating + "sugar" html syntax framework to build emails. Looks like you can add MJML into the template manager[1], but I guess you'll have to build your own workflow.

I think you misunderstood what's the purpose of MJML :

(this passing variable is not clear in your doc by the way)

MJML is NOT a _templating_ language, it's only a markup language. If you need to handle variables, you'll have to use something like handlebars before/after rendering the MJML. Note that conditionnals structure should be applied __before__ transpiling MJML into HTML, because MJML has internal logic to compute how much column you have in a section to define width and many other things.

MJML is like an HTML document but you can't put MJML inside an HTML document, the flow should be : Templating + Pug should produce MJML and MJML will produce your final HTML.

MJML is already taking care of inlining your style when using mj-style so you don't need to take care of that.

I hope i'd answered your concerns

[1] : https://github.com/crocodilejs/node-email-templates/blob/master/src/template-manager.js

Ok thanks for your response.

If I were to compile it statically (from command line) with gulp pug + gulp mjml it is ok. I started from a demo project in github
https://github.com/herrkessler/jade-mjml
It works.

But the problem is that I do not compile statically but dynamically when my node server ask to send a email with specific variables, the name of the user I send, ...

So node-email-templates is in charge of compiling the email html from :

  • an file in a template format like pug
  • a css or scss file
  • some variables passed to the template.

So I really do not know when to run the mjml > html compilation.
Per aps at this end ? When node-email-templates is about to send the email, I could convert mjml tags to html tags ?
Does the file need to have only mjml tags ? Or could have other normal html tags ?

Do you have a prefered workflow to integrate to a node.js express server ?

Here is my file that actually send the email
May be I can use the mjml lib where I writed
debug(renderedTemplate.html);
With the complete html (that would in this case contain mjml tags.
And before sending.

'use strict';
const config = require('../../config')();
const debug = require('debug')('emails-send');
const nodemailer = require('nodemailer');
const mailGunTransport = require('nodemailer-mailgun-transport');
const path = require('path');
const templateDir = path.resolve(__dirname, '..', '..', '..', 'templates', 'emails');
const EmailTemplate = require('email-templates').EmailTemplate;
//https://github.com/orliesaurus/nodemailer-mailgun-transport
const transport = nodemailer.createTransport(mailGunTransport(config.emails.mailgun.auth));
// Global email settings
const emailSettings  = config.emails.from;

/**
 * Send the email trough mailgun api
 *
 * @param {object} email : email object
 *  email.to destination email
 *  email.subject email subject
 * @param {string} templateId : identifier of the template to use
 * @param {object} templateVars : Object of variables to send to the template
 */
exports.sendEmail = function (email, templateId, templateVars) {
  return new Promise(function (resolve, reject) {

    var template = new EmailTemplate(path.join(templateDir, templateId));

    template.render(templateVars, function(err, renderedTemplate) {
      if (err) {
        debug('there was an error rendering the template.');
        return console.error(err)
      }
      debug(renderedTemplate.html);
      transport.sendMail({
        // mandrillOptions: {
        //   template_name: templateId,
        // },
        from: emailSettings.from,

        to: email.to,
        subject: email.subject,

        bcc: emailSettings.bcc,

        html: renderedTemplate.html,
        text: renderedTemplate.text
      }, function(err, info) {
        if (err) {
          debug(err);
          reject(err);
        }
        else {
          debug(info);
          resolve(info);
        }
      });
    });
  });
};

Ok it works
I passed my html resulting for pug compilation
in

const mjml = require('mjml');
template.render(templateVars, function(err, renderedTemplate) {
      if (err) {
        debug('there was an error rendering the template.');
        return console.error(err)
      }
      debug(renderedTemplate.html);

renderedTemplate.html is looking like that

<mjml><mj-body><mj-section><mj-column width="100%"><mj-image width="100" src="http://res.cloudinary.com/hrscywv4p/image/upload/c_limit,h_1440,w_720,f_auto,q_90/v1/206926/LOGO_BAM_knz1yz.png"></mj-image><mj-divider border-color="#F45E43"></mj-divider><mj-text font-size="20px" color="#F45E43" font-family="helvetica">Bam Architecture</mj-text></mj-column></mj-section><mj-section><mj-text font-size="20px" color="#F45E43" font-family="helvetica"></mj-text></mj-section><div>Hello Sebastien Lucas</div><div>BAM vous propose une offre !</div><div>Dites-nous sous 24 heures si vous participer.</div><div class="call-action"><span>Voir le</span><a href="http://dev.bam.archi/offer/22?archiSelect">d茅tail de l'offre</a></div></mj-body></mjml>

then passed it to mjml.mjml2html function

      let mjmlRendered = mjml.mjml2html(renderedTemplate.html);

and get like this

{ errors: [],
  html: '<!doctype html>\n<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">\n<head>\n  <title></title>\n  <!--[if !mso]><!-- -->\n  <meta http-equiv="X-UA-Compatible" content="IE=edge">\n  <!--<![endif]-->\n<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\n<style type="text/css">\n  #outlook a { padding: 0; }\n  .ReadMsgBody { width: 100%; }\n  .ExternalClass { width: 100%; }\n  .ExternalClass * { line-height:100%; }\n  body { margin: 0; padding: 0; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }\n  table, td { border-collapse:collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; }\n  img { border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic; }\n  p { display: block; margin: 13px 0; }\n</style>\n<!--[if !mso]><!-->\n<style type="text/css">\n  @media only screen and (max-width:480px) {\n    @-ms-viewport { width:320px; }\n    @viewport { width:320px; }\n  }\n</style>\n<!--<![endif]-->\n<!--[if mso]>\n<xml>\n  <o:OfficeDocumentSettings>\n    <o:AllowPNG/>\n    <o:PixelsPerInch>96</o:PixelsPerInch>\n  </o:OfficeDocumentSettings>\n</xml>\n<![endif]-->\n<!--[if lte mso 11]>\n<style type="text/css">\n  .outlook-group-fix {\n    width:100% !important;\n  }\n</style>\n<![endif]-->\n<style type="text/css">\n  @media only screen and (min-width:480px) {\n    .mj-column-per-100 { width:100%!important; }\n  }\n</style>\n</head>\n<body>\n  <div style="margin:0px auto;"><table role="presentation" cellpadding="0" cellspacing="0" style="font-size:0px;width:100%;" align="center" border="0"><tbody><tr><td style="text-align:center;vertical-align:top;direction:ltr;font-size:0px;padding:20px 0px;"><!--[if mso | IE]>\n      <table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;width:NaNpx;">\n      <![endif]--><div class="mj-column-per-100 outlook-group-fix" style="vertical-align:top;display:inline-block;direction:ltr;font-size:13px;text-align:left;width:100%;"><table role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0"><tbody><tr><td style="word-break:break-word;font-size:0px;padding:10px 25px;" align="center"><table role="presentation" cellpadding="0" cellspacing="0" style="border-collapse:collapse;border-spacing:0px;" align="center" border="0"><tbody><tr><td style="width:50px;"><img alt="" title="" height="auto" src="http://res.cloudinary.com/hrscywv4p/image/upload/c_limit,h_1440,w_720,f_auto,q_90/v1/206926/LOGO_BAM_knz1yz.png" style="border:none;border-radius:;display:block;outline:none;text-decoration:none;width:100%;height:auto;" width="50"></td></tr></tbody></table></td></tr><tr><td style="word-break:break-word;font-size:0px;padding:10px 25px;"><p style="font-size:1px;margin:0px auto;border-top:4px solid #F45E43;width:100%;"></p><!--[if mso | IE]><table role="presentation" align="center" border="0" cellpadding="0" cellspacing="0" style="font-size:1px;margin:0px auto;border-top:4px solid #F45E43;width:100%;" width="100"><tr><td style="height:0;line-height:0;">聽</td></tr></table><![endif]--></td></tr><tr><td style="word-break:break-word;font-size:0px;padding:10px 25px;" align="left"><div class="" style="cursor:auto;color:#F45E43;font-family:helvetica;font-size:20px;line-height:22px;text-align:left;">Bam Architecture</div></td></tr></tbody></table></div><!--[if mso | IE]>\n      </td></tr></table>\n      <![endif]--></td></tr></tbody></table></div>\n</body>\n</html>' }

and I coud pass to node mailer the final version

transport.sendMail({

        from: emailSettings.from,

        to: email.to,
        subject: email.subject,

        bcc: emailSettings.bcc,

        html: mjmlRendered.html,
        text: renderedTemplate.text
      }
Was this page helpful?
0 / 5 - 0 ratings