Introducing mjmx: a custom JSX runtime for mjml
In my opinion, one of the best things that happened because of React, is JSX.
I know many people seem to hate on JSX, but I like it.
With JSX, or more specifically TSX, one can write typesafe html, instead of relaying on libraries like handlebars, which is a great library, but it keeps your code and your variables separated.
So every change in variables needs to be synced to the view-model, or worse, risk in runtime errors / blank values.
Recently, I’ve been doing some server-side rendering, and was using @kitajs/html as HTML JSX runtime.
@kitajs/html is great, because it does not bring react or react-dom with it.
And when I got to emails, I decided to try react-email and mjml-react.
But I was disappointed in both.
First of all, I like mjml.
It’s a great syntax, and it’s battle tested.
react-email seems to not use mjml, but instead re-implement email rendering themselves.
Now, react-email is a product by Resend, and the guys at Resend are doing email sending, so I bet they know how to render emails properly.
Despite this, there is a second issue: I don’t like React, and I don’t want to bring react and react-dom just to render a couple of emails.
So I decided to try to write a custom JSX runtime for mjml.
And that’s how @mjmx/core was born: mjmx Github.
It’s a custom JSX runtime that has no dependency on react or react-dom.
It implements the minimum needed runtime to support all the mjml tags, as well as the allowed HTML tags.
And with it, writing mjml becomes a breeze:
const Email = ({ name }: { name: string }) => (
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text font-size="20px" color="#333">
Hello {name}!
</mj-text>
<mj-button href="https://example.com">Click me</mj-button>
</mj-column>
</mj-section>
</mj-body>
</mjml>
);
const { html, errors } = render(<Email name="World" />);
Under the hood, render is just a proxy to mjml2html from the mjml library.
This means that mjml compilation happens with each email you render.
I would like to outsource the mjml compilation into a build / transpile step, but with the current approach it is not possible, unless you decide to re-implement email rendering yourself.
I hope you will find this library useful. Don’t forget to star it on GitHub, and feel free to reach out to me for questions, or open an issue / PR for suggestions and improvements.