Ever since I introduced gomjml to the public, it has undergone quite significant development. To the unsuspecting eye, the end result may seem the same, but under the hood, almost every single nook and cranny of the library has changed. For the better. And, I am far from finished, even though the library is fully functional and usable as it is.
Introducing gomjml: MJML for Go Developers
gomjml is a native Go implementation of the MJML email framework, making responsive email design faster and easier for Go developers.
This is only a brief update, to give people a heads-up on where it’s going.
Golang Weekly — the big catalyst #
I wrote the original blog post announcing the first public release of gomjml, in early August. It wasn’t until the 20th, when I saw a huge influx of visitors to my site, when I realized they all came to it because of the library. Turns out, I had been featured in Golang Weekly, and I was totally unaware of it.

The devil is in the details #
It would be unrealistic for me to believe that I’d get gomjml production-ready in such a short amount of time. I’ve been in this game long enough to expect the first bug reports soon after I open-sourced it.
And, I did get them, but they were far more subtle than I’d expected. See, on the surface, the library would work just fine. It would produce the HTML content I needed to satisfy most email clients, and I was even able to integrate it in some of our own work. But try to do something a little more unorthodox, and you’d see these subtle visual inconsistencies. Or worse, there’d be this one obscure version of Outlook that won’t render the email nearly half as well as the original MJML would.
But where was the problem? After all, I had nearly 400 integration tests comparing the output against that of MRML (the Rust implementation of MJML). They all passed at all times, so I assumed that things must be alright. Right?
From MRML to MJML #
Had it occurred to me to think that, perhaps, MRML itself was not 100% compliant with MJML, I’d have switched way earlier. However, by the time I realized it, the differences in the produced HTML, subtle as they were to the naked eye, were so substantial for the test and validation pipeline I’d built, that while fixing one test case, it ended up failing a hundred more.
I made a couple of attempts to patch the MRML code itself with the help of Claude Code, but I don’t like messing with Rust at this point. If I didn’t come up with a decent RFC that the maintainers of MRML would accept, I’d have to end up maintaining a fork of my own. Not something I have the time and energy to do at the moment.
So, I had to the only reasonable option — the one I should have gone with from the very beginning. I decided to go for full feature parity with the original MJML compiler itself.
I am getting there (slowly) #
This is a painstaking process of having had to disable all the tests (more than 500 at this point), enable one, get it to pass, and then move on to the next one. Luckily, I am almost 70% done by now (and of course, Claude Code has helped a ton), so from the next release onward (hopefully, in the next couple of weeks), I will be able to say that gomjml is fully compliant with MJML.
Have something to say? Join the discussion below 👇
Want to explore instead? Fly with the time capsule 🛸
You may also find these interesting
Introducing gomjml: MJML for Go Developers
gomjml is a native Go implementation of the MJML email framework, making responsive email design faster and easier for Go developers.
Display your Claude Code Token Usage on Your Mac's Toolbar
A simple Python script and xbar setup to monitor Claude Code token usage directly in your macOS toolbar.
Hitting the Brakes on Claude Code
Prevent Claude Code from burning tokens aimlessly. Slow things down with a simple shell trick.
Why I Made Peace With Go’s Date Formatting
If we’re all going to google it anyway, we might as well google something that makes sense.

