Having gone through the same process of trying to find 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.
It turns out that MJML is just simple XML and maps quite well to JSON. Rendering from JSON is already supported by MJML, but only rendering the whole document (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 programmatically (so no need for manual escaping or worries about XSS/injection), and I created a module mjml-dynamic
to solve this problem.
npm i --save mjml-dynamic mjml
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';
import readFile from 'fs/promises';
const replacers = {
myId: {
content: 'new text content',
attributes: { color: 'red' },
},
};
const mjml = await readFile('template.mjml');
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>
You can even use it in combination with mjml-react
to render parts of your template using React.
See more examples here
mjml
. That was a critical feature and rendering twice wasn't ideal... so we just went for much more practicals react components! – Stucker