Closed justinmchase closed 5 years ago
99% of systems using mjml will have some form of data binding in conjunction with mjml.
I don't think that "99%" of people are using a templating lang conjoining to MJML, but it's still a good amount of people. And for today I think the latest improvment over mj-raw on MJML4 are really well received and ease the "plug and play" side to your current framework/solution.
About a "live" preview with data, I think with gulp or any task runner it's easy enough to handle templating before OR after mjml transpilation
I'm just saying that this was the path html went down a long time ago and it has the drawbacks mentioned above but its too late for html. Since then there have been some improved techniques invented which is essentially to have integrated databinding semantics. Take a look at DataBinding in xaml for example.
I think you're overthinking the problem here. MJML aim to reduce the pain behind email clients specificities, not to solve an endless quest for the all-in-one solution/workflow. Being open is nice, we use today an external project for HTML minification, and same for CSS Inlining, both project encounter some of problems we have too (avoid messing with non standard xml/html in valid html/xml document). Adding a templating language in top of current mjml-core would require to answer again some of the problems that are already solved by almost every template language today.
View the generated mjml, which is the product of running it through a custom transformation pipeline and composed with data but now has the downside of not being the original files in code. Therefore if you try to edit that output it will be lost and somehow needs to be translated back into the original source.
Or just adapt the workflow to inject data only for preview, we have this working with premailer on Rails, it's even simpler on Nodejs env, and I don't think it would be too hard to implement in any framework :)
This has been discussed a number of time and the conclusion will still be the same : Adding templating lang/directive would bloat the langage itself and the learning curve too. There's already enough templating language out there to cover everyone usage.
Last quote :
Your tooling will always be limited or disconnected from the original source code
For us it's a good thing, MJML is just a transpiler that remove the complexity behind HTML email, in some way we're adding some, but I think after 3+ years of activty, I think it's safe to assume that we also solve a good amount of issues in HTML development
I knew you were going to say this but I wanted to write this up anyway because I think a lot of people coming here are experiencing the same dissonance and have this same expectation. If you didn't put it into this layer that would be fine but it seems like the layer should exist and the various tools need it to be useful.
Its just confusing when you see the two developer tools that exist but its not clear how they could ever be used since your actual mjml files end up being templated.
I think it's safe to assume that we also solve a good amount of issues in HTML development.
Yeah I mean this is great, the complexity of email html and style inlining is really gross and this is a majorly helpful tool. Based on some of these recurring questions here though it does seem like an issue that people encounter, as we are right now.
A designer hands me massive hand rolled mjml files full of lorem-ipsum and now you have to turn them into something that renders from actual data. Ok handlebars to the rescue. Ok now a new contractor wants to update them all. So we hand them our template files but they have no idea what to do with them because the mjml tool can't visualize them since they're all handlebars. So they basically struggle to make it work by removing all the templates and inlining their own fake data and then much later then hand me the templates back and now I have to manually figure out what they're doing again and recovert it back into a templated thing for data binding.
Meanwhile developers are tweaking the templates over time so they originals are no longer valid and there are no good tools for viewing the end result without rolling our own solution for rendering email previews and live data sampling... I mean I'm just saying everyone using this is having the same problems and all coming up with their own half-baked solutions I am pretty sure. I've done it before with even worse tools so when I saw this I was happy and it sounded great ... but its just missing the point just a little bit.
Its true this addresses one gross problem about emails but the other important problem about emails is visual feedback at design time, which I can see there has been an attempt to address, but the solution misses the very big picture related to data binding which is a crucial aspect of emails.
Ok so, if your workflow is bad, is it safe to assume that MJML is the culprit ? I don't want to sounds like an ass here, but let's be realistic. Even if we provide a built-in way to iterate/condition over data, how will you feed those data into template then, again via a script, and at a scale of a company ?
Meanwhile developers are tweaking the templates over time so they originals are no longer valid
Here it's another problem, and we can't really do something about it, MJML to HTML is a one way trip. If I use typescript/babel and then I edit the output of it, would I blame typescript to give me a chance of put those changes back into my code base ? I don't think so. Education is key here, don't touch any of the final html if it's not part of an automated process as we do today for any transpiled language.
I think, here, what you need to do is simply to reconsider how you build MJML templates and provide builds with data. It's like not really hard to do a small backend or even some convention with a simple yaml/json/xml/... whatever format and build a final HTML with it.
Handlebars itself won't fit your need, MJML itself neither. You can buy any ingredients of a recipe, you still have to cook it at the end of the day to have a meal, it's basically the same here.
but the solution misses the very big picture related to data binding which is a crucial aspect of emails.
No one could end up with a solution that satisfy everyone, so I think, build your own and capitalize on it. If you'd feel that anyone could have the same issue as you, you can just also build a thing on top of MJML on your own and use/release it.
It's already hard to find time to keep an open source project active, so let's not go too far of the initial scope.
Again sorry if I sound really aggressive in this reply, but there's no bad intention behind it.
I'm closing the issue because I think the subject is way to big. The discussion would lead to a dead end anyway, where there's no real consensus or it would be tied to the way your company work at the end
how will you feed those data into template then, again via a script, and at a scale of an company ?
I'd say two ways. Dynamically at render time and also have a way to declare it so that the dev tools can dynamically generate sample data during viewing time.
Take this modified canonical snippet from your README as an example:
import mjml2html from 'mjml'
const data = {
title = 'Example Title',
items: [
{ name: 'foo', description: 'this is an item' },
{ name: 'bar', description: 'this is another item' }
}
/*
Compile an mjml string
*/
const htmlOutput = mjml2html(`
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text field="title">
Hello World!
</mj-text>
</mj-column>
</mj-section>
<mj-section>
<mj-row field="items">
<mj-column>
<mj-text field="name">
Unnamed
</mj-text>
</mj-column>
<mj-column>
<mj-text field="description">...</mj-text>
<mj-column>
</mj-row>
</mj-section>
</mj-body>
</mjml>
`, { ...options, context })
/*
Print the responsive HTML generated and MJML errors if any
*/
console.log(htmlOutput)
Next I would also support declaring an element which does not render as html but can be used by tools to create the dynamic data. So if your file is example.mjml
:
<mjml>
<mj-data type="lorem-ipsum">
<mj-data-phrase field="title" />
<mj-data-list field="items" min="1" max="10">
<mj-data-phrase field="name" />
<mj-data-paragraph field="description" />
</mj-data-list>
</mj-data>
<mj-body>
<mj-section>
<mj-column>
<mj-text field="title">
Hello World!
</mj-text>
</mj-column>
</mj-section>
<mj-section>
<mj-row field="items">
<mj-column>
<mj-text field="name">
Unnamed
</mj-text>
</mj-column>
<mj-column>
<mj-text field="description">...</mj-text>
<mj-column>
</mj-row>
</mj-section>
</mj-body>
</mjml>
This would render to identical html as the previous example, the <mj-data>
section would just get ignored, but tools could use that metadata to be able to open this file as is and render it with sample data. Designers will be able to edit the original source files and application code is also rendering from the exact same code only with production data. The key is that this puts mjml in the front of the transform pipeline instead of the middle, which allows the code to be edited and viewed as-is without needing manual translations after the designer hand off. The paradigm of the existing tools will work this way, whereas they will not necessarily work once you have templating.
MJML to HTML is a one way trip.
Right but Handlebars to MJML is also a one way trip for the same reason. That's the disconnect the two design tools are not accounting for. They are both showing the MJML on the left and the html on the right, which is encouring people to write the mjml in those tools but yet its being generated by a different tool... you've got to edit the source of truth not some intermediate form down the line. If mjml isn't that then you get into this situation where people come to your github issues page and ask the same dumb question over and over :)
If I use typescript/babel and then I edit the output of it, would I blame typescript to give me a chance of put those changes back into my code base ?
No, I wouldn't but if the typescript team told me to go use handlebars to generate typescript first then I'd have a serious problem.
...don't touch any of the final html if it's not part of an automated process as we do today for any transpiled language.
Exactly, just to be clear here though by taking the position that people should use handlebars to generate mjml then you are putting mjml itself in the position of being a transpiled language that is generated as part of an automated process that shouldn't be touched... which is where the dissonance is coming from and thus this discussion.
Am I making sense? You're taking the position that mjml is output from a different tool. Like html is output of mjml, like js is output from typescript. You said it yourself, output of a tool should not be edited directly because its a one way trip. Yet mjml is very commonly output of a different tool.
No one could end up with a solution that satisfy everyone, so I think, build your own and capitalize on it.
I don't think you need to make everyone happy, everyone is already not happy 😆
It's already hard to find time to keep an open source project active, so let's not go too far of the initial scope.
Going too far on scope is bad... so is not going far enough. I'd recommend weighing the idea on its merits independent of other considerations first.
I think this is a pretty modest proposal and a pretty commonly expected feature that would improve the usability.
If you though this should be in a different layer I totally get that, mjml-data
or whatever. Thats a technical details. But bless that and make the tools point at it. There are ways to solve it.
Designers will be able to edit the original source files and application code is also rendering from the exact same code only with production data.
They are both showing the MJML on the left and the html on the right, which is encouring people to write the mjml in those tools but yet its being generated by a different tool...
Making a gulp task on your own will make this available with even live reload, and without any addition to the langage and it can be shared & customized accordingly to your need.
MJML App is an electron app, open source, you can clone it add a tab window with json data put handlebars (or whatever templating language in js or in any lang) and then you have it... Problem solved, without even toutching a single line of code of mjml-core.
The solution you provide is just ugly, with limited set of input data structure, and you're not able to scale it for multiple source if you want to test with multiple cases. I can see so many drawback with only one given implementation and so many things that people would want and won't be available. We don't want to close doors to interoperability too, so let's keep those concerns away, it's already complex enough.
Am I making sense? You're taking the position that mjml is output from a different tool. Like html is output of mjml, like js is output from typescript. You said it yourself, output of a tool should not be edited directly because its a one way trip. Yet mjml is very commonly output of a different tool.
Most of templating langages doesn't care if it output HTML, XML, or whatever markup langage they're just abstracting concept of templating. It's a simple I/O interface that you can plug how many time you want to.
Here it's a matter of how applying/manipulating data on a given document. Our stance is to let competent libraries to do the manipulation part and just let MJML to render static piece of content nothing more.
No, I wouldn't but if the typescript team told me to go use handlebars to generate typescript first then I'd have a serious problem.
I don't think you've clearly understand what was the analogy here.
The key is that this puts mjml in the front of the transform pipeline instead of the middle,
But It is in your mind, not in ours. We want to keep MJML as a markup lang, not a templating one.
MJML is a markup language [...] designed to reduce the pain of coding a responsive email.
It's clearly the first phrase of the readme nothing more, nothing less as said earlier we're not an all in one solution.
Going too far on scope is bad... so is not going far enough
I prefer using a good screwdriver instead of a cheap swiss knife that will break as soon as I start to use it.
I think this is a pretty modest proposal and a pretty commonly expected feature that would improve the usability.
It's not "modest", it's a really big thing to do, parsing template, replacing stuff, adding conditionnal & loop & many more to come. It adds a whole new level of complexity in the code base, and a new level of logic that you have to design to be working with custom components & maybe a LOT more new interface developer friendly.
Maybe our position isn't the best but at least you can use MJML today with any lang/framework or product. Like it was done first by Mailjet and you can still use it on different ESP templating lang like Mailchimp, and many templating language Mustache, Handlebard, PugJS, ... Most of them are already used, battle tested, robust and cover every use case you'd want to.
Contributions here are really low, and you would expect us to build a gigantic mammoth. It's free & open source, so you're free to fork MJML and do those features if you want to.
MJML is a markup language [...] designed to reduce the pain of coding a responsive email.
I'm just saying this is a pain point, it would reduce pain to add templating support at some layer. If this is the goal of this tool then I am asking you to please reconsider your position.
Well this discussion isn't really going anywhere, I think I've already explain ourselves on why we won't do such thing, if you want so, as said it's an open source project so feel free to fork it.
Related to #1457
+1 on this...a very barebones if-else conditional (like vue.js's v-if and v-else) would be really great.
The maintainers of mjml seem to be missing the point here. Nearly all automated email that is sent is dynamic - it will have the recipients name or some other personalized element.
The problem developers have is that it is hard to send dynamic HTML emails that will display properly in most email clients. MJML only solves part of the problem.
If you don't want to put templating into MJML, you don't have to, you are the maintainers. You could at least provide an example in the docs of how one could correctly set up a build pipeline that actually solves the entire problem.
There's dozen of package/blog post on how to achieve that. There like hundred of templating language, and we won't be able to cover everything in documentation. A small google search about your loved framework/templating lang will probably cover your need :
And some companies like Mailjet did integrate fully MJML in their ecosystem. And we even done an free API https://mjml.io/api (even if registration is a bit broken #1751 but it will be back soon)
If you want to start adding something in the doc because you find it useful for the community go for it. It's open source, not everything need to be done by "maintainers". Every MD files are here https://github.com/mjmlio/mjml/tree/master/doc
Robert,
It occurs to me to wonder about the goals for the MJML project.
You make an articulate case that they are failing at the goal of making all of life easy for email developers. You correctly observe that "MJML only solves part of the problem."
While I'm no architect, I've heard many of them (particularly from the Posix world) call for each piece of code "to do one thing and do it very well".
That makes sense to me.
The MJML team and their close followers on the Slack channel have demonstrated there that they are among the strongest of experts in email development. They stay current on the entire field in ways most of the rest of us only aspire to do.
I rejoice that they choose to share their particular expertise with us. It significantly lightens our load. They are contributing to one area of great expertise and doing it very well.
I don't know whether they are similarly expert in templating. Perhaps they are expert in templating with Gulp and no other; it works for them. In that case, they recognize their difference between contributing to email development and contributing to templating. Perhaps further development in templating is best left to templating experts.
There seem to be a number of those, each catering to different needs. Given that, the challenge for the rest of us is to survey that field and find the entry there that best serves our needs. Pair it with MJML and we have an environment perfect for us.
I understand your pain. I believe the MJML team understands, too. All the best in your continuing efforts.
. . . Garry
From: Robert Sweeney notifications@github.com Sent: Monday, November 11, 2019 7:00 PM To: mjmlio/mjml mjml@noreply.github.com Cc: Subscribed subscribed@noreply.github.com Subject: {Disarmed} Re: [mjmlio/mjml] Templating needs be a first class feature of mjml (#1630)
The maintainers of mjml seem to be missing the point here. Nearly all automated email that is sent is dynamic - it will have the recipients name or some other personalized element.
The problem developers have is that it is hard to send dynamic HTML emails that will display properly in most email clients. MJML only solves part of the problem.
If you don't want to put templating into MJML, you don't have to, you are the maintainers. You could at least provide an example in the docs of how one could correctly set up a build pipeline that actually solves the entire problem.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/mjmlio/mjml/issues/1630?email_source=notifications&email_token=ADLRFX6JSMO7UHDWD2QHFDTQTH5X7A5CNFSM4H3K4CLKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEDYVABQ#issuecomment-552685574 , or unsubscribe https://github.com/notifications/unsubscribe-auth/ADLRFXZ6CXCN6BVKV2Y7ENTQTH5X7ANCNFSM4H3K4CLA . https://s3.amazonaws.com/msv5/images/spacer.gif
[ { "@context": "http://schema.org", "@type": "EmailMessage", "potentialAction": { "@type": "ViewAction", "target": "https://github.com/mjmlio/mjml/issues/1630?email_source=notifications\u0026email_token=ADLRFX6JSMO7UHDWD2QHFDTQTH5X7A5CNFSM4H3K4CLKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEDYVABQ#issuecomment-552685574", "url": "https://github.com/mjmlio/mjml/issues/1630?email_source=notifications\u0026email_token=ADLRFX6JSMO7UHDWD2QHFDTQTH5X7A5CNFSM4H3K4CLKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEDYVABQ#issuecomment-552685574", "name": "View Issue" }, "description": "View this Issue on GitHub", "publisher": { "@type": "Organization", "name": "GitHub", "url": "https://github.com" } } ]
I did look into creating a plugin at one point to help me achieve my goals. It does appear that mjml could actually support the goal I was going for, with regards to adding some new kinds of elements with dynamic behaviors. I appears that I could simply create plugins for all the desired elements but the mjml rendering tool may need some work to really enable plugin work of that complexity. It was having issues where making changes to the plugins weren't picked up without some complex steps to get it to refresh, display errors, and debugging your plugin were complex. It didn't seem like an enormous amount of work was needed to make some improvements to the tool that would help a lot, I just don't have the time at the moment to dig into it.
We ended up building our own pipeline, as recommended, which involved converting all of our mjml templates into react pages which we transpile into mjml and then down to html. I don't love it but it is working. The main problem is that I had to basically throw away the mjml templates that our designer gave us and convert them to something else manually and I can't see the templates without rendering them through our site.
Thanks all.
There's dozen of package/blog post on how to achieve that. There like hundred of templating language, and we won't be able to cover everything in documentation. A small google search about your loved framework/templating lang will probably cover your need :
- https://github.com/sighmon/mjml-rails
- https://github.com/liminspace/django-mjml
- https://github.com/mjmlio/gulp-mjml + any JS templating gulp plugin (provided by MJML team)
- https://github.com/notFloran/mjml-bundle
- https://github.com/julien-leclercq/bamboo_mjml
- https://github.com/rennokki/laravel-mjml
- https://github.com/wix-incubator/mjml-react
And some companies like Mailjet did integrate fully MJML in their ecosystem. And we even done an free API https://mjml.io/api (even if registration is a bit broken #1751 but it will be back soon)
If you want to start adding something in the doc because you find it useful for the community go for it. It's open source, not everything need to be done by "maintainers". Every MD files are here https://github.com/mjmlio/mjml/tree/master/doc
I don't know if this blog post is contributing to calls for this to be addressed because it seems to encourage use of MJML without another templating system like Handlebars, Twig etc... https://www.mailjet.com/blog/news/templating-language-tutorials-welcome-email/
I guess it is not surprising that the article and related Github one at https://github.com/mailjet/mailjet-apiv3-templating-samples/tree/master/tutorials/welcome would focus on using the Mailjet API rather than combining with a local templating system.
Coming from that article to here, your suggestion here for templating integrations could perhaps be given more prominence in the README for the project with an Integrations section, just above or below the Section on the API, rather than just being somewhat discoverable from the link provided in the sentence "For more tools, check the Community page"
It might save you some time rehashing the philosophy being taken, and encourage people to share their implementation of MJML using such an integration, along with their documentation of it.
I know that this is my biggest barrier to jumping in and getting started with Handlebars / MJML.
Thanks for sharing all you do @iRyusa
We're discussing about a new page in the doc where we show a minimal setup with gulp+handlebars and point to those libs too @andrewperry you're right about this. 👍
We ended up using mjml-react instead of handlebars. And then we added *.tsx
files into our app which contain mjml elements in a react component.
The downsides to it are that our source files are not mjml and so we cannot view the file in the mjml developer tool. Instead we have a route that just lets us render to html and view as html in the browser in local develop mode.
We're discussing about a new page in the doc where we show a minimal setup with gulp+handlebars and point to those libs too @andrewperry you're right about this. 👍
That sounds great, perhaps along the lines of https://medium.com/@toru_aine/how-to-get-more-out-out-of-mandrills-html-email-templates-by-using-handlebars-7c51feff3764 ?
Emails without dynamic contents are 1997.
Definitely agree that MJML needs at least a proper handlebars/moustache integration. I have a workaround how I use handlebars in my emails, but again I have to build the MJML first and then apply hbs logic. Thus I cannot use MJML within dynamic content which forces me to write plain html. And as far as I understand MJML is the markup abstraction of obsolete and painful email html.
Thus I cannot use MJML within dynamic content which forces me to write plain html.
You can, just take some times to setup a proper workflow with your favorite templating language and MJML
We've replied to this plenty of times already. @mercteil what you're saying is basically like saying:
Web pages without easy DOM manipulation is 1995.
HTML needs built-in DOM manipulation.
There are a lot of libraries like jQuery making DOM manipulation easy that you can include in your web page. Similarly, there are a lot of templating languages that you can use in combination with MJML. The answer is certainly not to have template language features as part of MJML like it is not to have DOM manipulation as part of HTML.
We'll publish soon a tutorial on how to set up a workflow with any templating language and MJML, which should hopefully help get started.
Note that there are already a lot of resources from the community on how to set this up, including articles (example: Using MJML with Twig) and client tools (example: email generator). A simple Github search for "mjml + your favorite template language" will also return a lot of results (example: "mjml handlebars").
@justinmchase
We ended up using mjml-react
So did I. Highly recommended.
Instead we have a route that just lets us render to html and view as html in the browser in local develop mode.
Are you able to give me an example of how you do this using React/Next.js?
Much appreciated!
@LB22 Sorry I don't have access to that code anymore but it was basically just a simple express route which essentially loaded the react component and used it to render down to html.
Possible psuedocode:
import mjml2html from 'mjml'
import { MjmlExample } from './email/templates/example'
import ReactDOMServer from 'react-dom/server';
app.get('/test-email', (req, res) => {
const data = db.example.sample() // get random data from the db
const mjml = ReactDOMServer.renderToString(MjmlExample({ data }))
const html = mjml2html(mjml)
res.send(html).status(200)
})
I'm definitely of the opinion that it's best to leave templating to template engines and let MJML focus on being good at solving the email problem.
For anyone using node, ETA is hands-down the simplest, best solution. Example: https://github.com/coolaj86/node-eta-demo/tree/mjml
In essense:
render.js
:
'use strict';
let mjml2html = require('mjml');
let Eta = require('eta');
Eta.configure({ tags: ['{{', '}}'], views: ['.'] });
let tmplText = Fs.readFileSync('welcome.eta.mjml.html', 'utf8');
let vars = require('./vars.json');
let mjmlText = Eta.render(tmplText, vars);
let html = mjml2html(mjmlText, {
beautify: true,
keepComments: true,
validationLevel: 'strict',
}).html;
console.log(html);
welcome.eta.mjml.html
:
{{ layout("layouts/transactional-email.eta.mjml.html") }}
<mj-section>
<mj-column>
<mj-text>Hello, {{= it.user.given_name }}!</mj-text>
</mj-column>
</mj-section>
layouts/transactional-email.eta.mjml.html
:
<mjml>
<mj-head>
<mj-preview>Preview: {{= it.preview }}</mj-preview>
<mj-title>Title: {{= it.title }}</mj-title>
</mj-head>
<mj-body>{{~ it.body }}</mj-body>
</mjml>
vars.json
:
{
"preview": "Read \"friend\" and enter...",
"title": "Hello from the Elves",
"user": {
"given_name": "John",
"family_name": "Doe"
}
}
(@coolaj86): I'm definitely of the opinion that it's best to leave templating to template engines and let MJML focus on being good at solving the email problem.
💯
I think that the docs should be expanded with the templating options. It will alert the user of the fact that templating is not a first-class citizen in this.
Wait.... so because MJML offers no templating, how do we actually integrate it safely?
Right now, our (Kotlin) backend has been taking generated .html
, adding templating tags, and then integrating data. In our (frontend) view, that's not a maintainable solution. We want to define data binding at the MJML level, and be able to test / bind data in both Node.js and Kotlin environments.
Does the MJML team at least have some guidance into a... typical best-practice workflow?
So far as I know, the MJML team has left that material available for authors of web articles and for documentation in the various templating engines.
I understand many developers who use both a templating engine and MJML embed templating code or data-field names in their MJML. Perhaps there are two ways they could go from here.
If the templating engine is happy to accept and create MJML files, developers could use the templating engine on an MJML file and create a set of new MJML files, one for each desired email. Then, they would run those files through MJML to get HTML for each email.
On the other hand, if the templating engine presumes an HTML file input, they'd run MJML first, to generate an HTML file still containing the templating language. Then, they'd run that file through the templating engine to create the HTML files for sending.
Those processes need details corresponding to the templating engine. We presume the documenters for those engines are best equipped to document the preferred processes.
So, as I said earlier here, the MJML engine converts from MJML to HTML and does it very well. Templating engines produce individualized emails from a template and a set of data-field values. The MJML team would hope you find a templating engine that most exactly your needs and does it very well.
@matthew-dean I too have a Kotlin backend that's sending MJML-based email, and I use the process @GarryFlemings described in his comment. The MJML source has template language expressions/directives embedded in it (Apache FreeMarker in my case) and it gets compiled to HTML as a build step. The "HTML" isn't actually the final HTML output, though; it's a FreeMarker HTML template file that then gets rendered at runtime.
At the risk of going a bit into the weeds with my specific setup, which of course would vary if you use a different runtime or a different template engine:
.ftlh.mjml
extensions..ftlh
files (the file extension that tells FreeMarker it's dealing with HTML). I use the gradle-node-plugin to run the MJML renderer in the build.*.ftlh.mjml
that is configured as file type FreeMarker with data language MJML. I thus get syntax highlighting + autocomplete for both FreeMarker and MJML (via the MJML plugin) at the same time.This setup has been working nicely for all our outgoing email messages.
I ended up using the same solution as described here https://stackoverflow.com/questions/43111628/mjml-template-interpolation-dynamic-data-context:
Yes, it has to parse the content twice but it hasn't made a difference in a practical sense. Handlebars is a tried and true templating solution that takes care of many security issues by default.
import { compile } from 'handlebars';
import { mjml2html } from 'mjml';
const template = compile(`
<mjml>
<mj-body>
<mj-container>
<mj-section>
<mj-column>
<mj-text>{{message}}</mj-text>
</mj-column>
</mj-section>
</mj-container>
</mj-body>
</mjml>
`);
const context = {
message: 'Hello World'
};
const mjml = template(context);
const html = mjml2html(mjml);
For what it's worth, I just started using mjml
this week and I can say the overall DX (developer experience) was not very good. I agree that the templating engine should live outside mjml
but here are a few things that for me didn't help:
mjml
itself is quite good but there is no example of any code and searching on Google led me nowhere usefulmjml-raw
why does this package even exist? https://www.npmjs.com/package/mjml-raw. Apparently packages for all mjml
tags exists.. this is very unusual. Also, the only code example I found is actually in this thread which is a closed Github issue...I don't know if anyone is working on improving these pain points but otherwise once implemented, the solution is quite good.
Having gone through the same process of finding the best way to add dynamic content to my MJML files while still being able to preview the templates using existing tooling, initially I just used handlebars, but after adding more complex content structures it started to get messy.
But it turns out that MJML is just simple XML and maps quite well to JSON. Rendering from JSON is already supported in MJML, but not parts of the document.
I came up with a technique that allows rendering parts of a .mjml
file programmatically, by manipulating the XML(JSON) structure (so no need for manual escaping etc): and so mjml-dynamic
was born!
Example:
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-button mj-replace-id="myId">
Some text
</mj-button>
</mj-column>
</mj-section>
</mj-body>
</mjml>
import mjml2html from 'mjml-dynamic';
const replacers = {
myId: {
content: 'new text content',
attributes: { color: 'red' },
},
};
const { html } = mjml2html(mjml, { replacers });
This will output the equivalent of the following MJML document:
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-button color="red">
new text content
</mj-button>
</mj-column>
</mj-section>
</mj-body>
</mjml>
This also allows copying a beatufiul template from https://mjml.io/templates, then modify it to your needs, then replace parts of it with your own programmatic structure, or even a mjml-react
sub-tree!
See also discussion from #2619
I came up with a technique that allows rendering parts of a
.mjml
file programmatically, by manipulating the XML(JSON) structure (so no need for manual escaping etc): and somjml-dynamic
was born!
Unless I misunderstood what you are proposing, you won't be able to have conditional blocks with this strategy.
Unless I misunderstood what you are proposing, you won't be able to have conditional blocks with this strategy.
Yes, you can just use javascript:
const replacers = {
myId: {
children: [
...(shouldShowText ? [{ tagName: 'mj-text', content: 'some conditional text' }] : []),
{
tagName: 'mj-table',
children: list.map((item) => ({
tagName: 'tr',
content: { tagName: 'td', content: item.name },
}]),
},
],
},
};
mjml2html(mjml, { replacers });
Was just wondering about all of this myself, apparently mjml has "preprocessors" that we could easily use to author templates using any language - from the docs: https://github.com/mjmlio/mjml?tab=readme-ov-file#inside-nodejs
Apparently ThoughtBot guys are using it like that: https://thoughtbot.com/blog/building-templated-emails-with-mjml
Overview
I believe that it is a mistake for this project to maintain an unopinionated position about templating. 99% of systems using mjml will have some form of data binding in conjunction with mjml. Correct me if I'm wrong but I don't think that creating completely static email by hand is a super common scenario.
Problem
Currently mjml has tools created for it to view the rendered html in various simulated test environments (mobile or desktop, etc.) This is great but if those tools have no idea about the templating transformations applied to it and the data that goes into it then you have one of two options:
For example this vscode-plugin: https://marketplace.visualstudio.com/items?itemName=attilabuti.vscode-mjml
It won't work on files with handlebars syntax mixed in and if I'm looking at the files my build step produces I won't be able to edit it in the left because its not the right code. Same for the desktop tool.
Currently the examples appear to be geared towards people creating completely static templates with hand rolled sample data and you cannot simply use that as is and now you have a disconnect between people making email templates and the files that actually need to go into the file system, resulting in manual translations.
Is your feature request related to a problem? Please describe. There are no built in semantics for data binding, conditionals or loops therefore tools can't be made to view templated mjml files easily and all the current tools are designed to edit or view the final product not the actual source code in the project.
Describe the solution you'd like Either just pick handlebars as a first class citizen or implement your own syntax to allow data binding.
Add a tag which be used to describe sample data for use by tools when rendering but is ignored at runtime.
Off the top of my head prototype:
Then the display tools could use a lorem-ipsum generator to generate objects which can be used to dynamically expand what is visualized into a fully static content.
Describe alternatives you've considered Using handlebars and then creating a html server which serves up the transformed mjml then try to point the dev tool at a url instead of the file system, use source maps to get the editor to map back to the original source files.
Additional Context I'm just saying that this was the path html went down a long time ago and it has the drawbacks mentioned above but its too late for html. Since then there have been some improved techniques invented which is essentially to have integrated databinding semantics. Take a look at DataBinding in xaml for example.
I used to work on design tools for Microsoft in both visual studio and on the "new" IE developer tools and I can tell you that this is an important aspect to have in the base language for all future languages. Your tooling will always be limited or disconnected from the original source code if you go down this path and you'll need the same problematic workarounds that html suffers from where you have to use source maps and you have to have developers who can translate between the templates and the mjml that designers create manually.