Write proper HTML emails without hating yourself

Writing HTML emails has to be the biggest annoyance for every single developer. For reasons, email clients can only use HTML like done in 1995, webfonts are not supported properly, CSS can be ignored (depending on the client), and some old parsers are completely horrible (I’m looking at you Outlook! Using the Word render engine was not the best idea ever).

HTML email horror

For inventid we experienced the issue first hand: each of our emails was around 1000 LOC of HTML madness. In order to properly send these out, we created .html.erb files, so we could use Ruby to determine which parts of an email to show, and apply localization and customization. Once the email was coded and properly tested using Litmus, we were off the hook for a little while.

However with each change came significant grievances. Working in nested tables of multiple levels is no joy, and often my brain would be completely occupied with keeping track of the nesting levels. Incapable of performing the changes I actually wanted to perform.

MJML incoming

A great thing about developers is that once they get fed up enough, they will something better. Luckily for us, this time the guys/girls from Mailjet beat us to it: they released mjml (which unfortunately does not stand for Michael Jackson Markup Language), which is also responsive for eg mobile clients.

We quickly realized this could be hugely beneficial! So we started to adopt it asap in a prototype. However we noticed some downsides

  • For good reason, mjml is purely a markup language. It has no support for variables for example.
  • The CLI (there is an api by now though) depends strongly on files. Not great for customized emails which can be sent by hunderds every minute
  • We’d have to repeat lots of email logic in many services and subservices. Getting rid of HTML emails in each service would really be a plus.

Maily incoming

Therefore we decided to build Maily: mjml as a service, based on React. Effectively you build your emails as a React webpage, Maily then renders it down internally to mjml for you, and returns the data as HTML.

As a result, it runs as an API services where your post your JSON data and get an HTML email as a result. Thus you do no longer need all this logic inside every single subsystem.

This offers some great advantages:

  • Created of HTML emails is handled by an stateless HTML service.
  • Coding in React is actually fun
  • Allow reuse of components
  • Enforce proper layouts across emails

An email for us now looks like this

Since we now have a proper email system, we can also automatically build and test our emails. Our internal repository uses maily as a dependency, and for each email type example json data is available. On every pull request we generate examples for our designers, which they can directly test. This data is also fed to Litmus, so we know how it will render for clients. Within a few weeks, all of our emails were redesigned and implemented in maily.

The email (of which the source is above) now renders like this.


Switching from HTML emails to Maily has provided us with the following benefits:

  1. Keep our sanity.
  2. Ensure that adding or modifying an email would not become work for a few days.
  3. Eliminate the rendering need in each service (similar like iaas I wrote about before).

Before I end this post, I’d really like to thank the people behind mjml! I know it may sometimes have its quirks, but knowing what I came from, I do not mind them.



Software Engineer. Lead software engineer @ Magnet.me, former CTO @ inventid.nl. General nerd. github.com/rogierslag

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store