Creating emails with the Maily API — A how to (part 1)

These blogpost will be different from my previous ones, as I will show you how to setup a beautiful maily environment on which you can develop you HTML and text emails, using React and MJML. In this how-to I will guide you through all the required steps.

At the end of all how tos, you will have:

  • Componentized emails, so you can easily reuse code
  • Fluid layouts, so things render well from phone to iMac
  • An email API service which generates HTML and text emails based on input JSON
  • Open tracking in your HTML email
  • Translated emails for every user
  • A command to easily generate all emails for you to check designs
  • A Docker image to move it to production easily

For this how-to I’ll assume your are familiar with Node.js (version 6+) and npm. If not, please familiarize yourself with these concepts first. Apart from that, we will use mjml itself, so you should be familiar what the different tags do (or willing to look it up in the mean time). It might be our emails are not technically valid mjml (componentization is a bit hard), but they will render correctly.

A repository for this blog is available on Github.

This code will depend on having a Node.js 6+ version, so ensure you are using at least Node.js 6!

Once done, you will have an internationalized email which renders perfectly on any device and client, and is generated by your very own API service. Additionally, it might be the first time you are having fun building HTML emails!

Getting started

First we will setup our development environment, consisting of a few folders. The structure will look like this

<your project folder>
└── components
| └── html
| └── text
└── test-results
└── json

We have a dedicated folder for HTML and text components. In general these might look alike a lot, but they have significant differences as well. Since a wrong abstraction is worse than code duplication, we will simply have some duplicate code. We might figure out a great abstraction later on!

Additionally we create a folder for our tests results (our final HTML, mjml, and text emails). Since we need input for those emails, we create an additional json folder as well.

Installing our dependencies

Node.js being Node.js, we definitely need some additional packages. Lets start of with the easy ones: react and maily.

npm init && npm install --save react@15.4.1 maily@3.1.1

In order to make our build chain slightly more efficient, we will also setup some babel stuff.

npm install --save babel-preset-es2015 babel-preset-react && npm install --save-dev babel-cli

Next we will ensure that babel properly loads these, so we add a .babelrc file with the following contents.

"presets": ["es2015","react"]

Now we are ready to build!

Creating our first component

For this email, assume that our designers have given us the following MJML code to work with (including some placeholder values).

When our devvy eyes look at this email, we notice some redundancies which will appear in other emails as well. So we can break this email down in different sections, which is exactly what I’ve done below

So lets start out with our header, since that component seems relatively easy.

In order to make our React code as simple as possible, we will be wrapping some of mjmls components with our own, so we can supply our own default values easily. For that, we start with the divider in the header.

Before we do that, we create a style.js file in the components folder. Here we will put some basic style later on so we can reference them. For now its enough to fill it with the following content

module.exports = {
colors: {
distance: {
fontSize: {
width: {
fonts: {

OK, now it is really time for our first component! Create divider.js in components/html with the following basic structure.

As you can see, we have made it possible to set the color of the divider using a React prop. The paddings on the left and right are taken from the style.js file.

So let’s update that file to resemble the proper values

module.exports = {
colors: {
green: '#8BC43F'
distance: {
small: 8,
fontSize: {
width: {
fonts: {

There it is, our first component! But to be honest, it is not that exciting yet. But that will come (trust me!).

Now we can continue building the header :)

OMG, now we have a completely cutomizable header (with a logo you can click), properly wrapped in the mjml syntax.

We can just put <Header /> anywhere to apply it to an email. We are getting somewhere!

The footer component

In the footer we notice that the mj-section and mj-column are repeated quite a lot in order to become full width. So we make that a component as well. Technically it’s not required, but I have to maintain this later: readable code is a plus.

Next up is the contact section (I’m leaving out the social section, you can add it yourself while practising). Since we will probably be rendering a lot of text in our emails as well, I made a convenience component for that as well.

Writing the envelope

mjml requires some basic stuff to be available, and we want our headers and footers in there anyway. So we create an envelope.js file.

By now we have written all boiler plate which we can forever reuse in our emails. Give yourself a drink, you have come far by now!

Time to create the actual content

Remember the actual content at the top? That one is now quite easy to implement.

Wiring it all up

Now we will add the final index.js file for HTML generation, and create the service index.js . We will also create a nice generation scripts so you can see everything easily. See for details this Github commit :)

Where do we stand

Now we have an REST API which emits email compatible HTML by using mjml. You can clone the repo, run npm install and babel-node test.js and open the test-results/invoice.html in your browser :)

Next up: adding text components, and localizing everything! Be ready for those follow ups in a few days.

Edit: See part two!



Software Engineer. Lead software engineer @, former CTO @ General nerd.

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