Building responsive email with MJML
Go to home page

Building responsive email with MJML

So I assume that if you're reading this article, you belong to one of three categories of people: The type which has never built HTML email but wants to get into it, the type which has built HTML email before and wants to find a better way to do it and then the type who just reads everything I write for some reason.

If you count yourself among those that have never built an HTML email before, count your blessings. The amount of work that has to go into these magical creations can be absolutely mind-numbing. HTML email is like the Wild West of web technologies.

I wish I was kidding about this, I really do. Building HTML email was one of my responsibilities at my first job before I got into freelancing and I feel like in those three years I aged by at least a decade. The amount of tiny, obscure things you have to remember is incredible. An affront to everyone even. We're talking inline styles, table layouts, transparency hacks, and so and so forth. And funnily enough Google, the company responsible for one of the most advanced browers, is one of the worst offenders.

No really, here's a list of CSS support in email. Be sure you sit well before reading it. It's a hoot and a half.

Of course for a while now there's been solutions like Foundation for Emails, emailframe.work and many others. But they're either terrible to set up or still require you to remember a good few pitfalls in HTML email. But a while ago, thanks to @pugson I learned about one framework that stood above all others.

It's called MJML and it rocks my socks.

It came a bit too late for my mainstay in email marketing, but I still use it for transactional emails. And this is your whirlwind guide to building HTML with it! So let's get started.

Installing and running MJML

Installing MJML is quite simple if you have npm or yarn installed. You need only run the following command and you're good to go.

npm install -g mjml

If you're not part of the web crowd and don't have MJML or if you just want to try it out and save some time, you can also use their online playground. I admit that I mostly use that one, simply because it's super convenient to see your email right next to your code.

To learn how to use the command-line tool in detail, be sure to check out the excellent documentation.

But as quick summary here are the commands you will most likely want to use

# Compile the MJML file and save it to an HTML file
mjml email.mjml -o email.html

# Watch an MJML file for changes and recompile automatically
mjml --watch email.mjml -o email.html

# Minify the compiled HTML to save space
mjml --config.minify email.mjml -o email.html

Building your first email

As an example email to build I will pick one made by yours truly. It's a magic link email I'm using for a meeting tracker app I'm currently building.

Magic link email layout

It's not particularly complex, so I think it's a perfect fit for a first go at MJML. First, let's take a look at the entire template as it stands and then let's take it apart bit by bit.

<mjml>
    <mj-head>
        <mj-font name="Montserrat" href="https://fonts.googleapis.com/css?family=Montserrat:400,700" />
        <mj-preview>Click the button inside to log into your account</mj-preview>

        <mj-attributes>
          <mj-text font-size="16px" line-height="24px" padding="0px 48px 16px" />
          <mj-button font-weight="bold" font-size="16px" background-color="#8e2de2" />
          <mj-divider border-color="#8e2de2" />
          <mj-all font-family="Montserrat, Helvetica, Arial, sans-serif" />
        </mj-attributes>

        <mj-style inline="inline">
          .link {
            color: #8e2de2;
            text-decoration: none;
          }
        </mj-style>
    </mj-head>
    <mj-body>
        <mj-section padding-bottom="0">
            <mj-column>
                <mj-image
                        padding-top="20px"
                        padding-bottom="20px"
                        width="200"
                        src="https://howmuchdoesthismeetingco.st/assets/logo-hmdtmc.png"
                />

                <mj-divider />

                <mj-text align="center">
                    <h1>Your magic link</h1>
                </mj-text>

                <mj-text>
                    Somebody requested a log in from your account. If that was you click the button
                    below to sign into your account.
                </mj-text>

                <mj-button href="https://howmuchdoesthismeetingco.st/token/abcdefg123456789">
                    Log in now
                </mj-button>

                <mj-text>
                    <br />
                    Have a wonderful day,<br />
                    <strong>Richard from HMDTMC</strong>
                </mj-text>

                <mj-divider />

                <mj-text
                        padding="8px 48px"
                        font-size="11px"
                        line-height="16px"
                        color="#999999"
                >
                    If the button up doesn't work, you can use this link:
                    <a
                            href="https://howmuchdoesthismeetingco.st/token/abcdefg123456789"
                            class="link"
                    >
                        https://howmuchdoesthismeetingco.st/token/abcdefg123456789
                    </a>
                    <br/>
                    Your login link expires after 1 hour. You can request another one at any time.
                </mj-text>
            </mj-column>
        </mj-section>
    </mj-body>
</mjml>

Okay, that was a lot of code and if you're not running yet, I'm proud of you. The most complex part is probably right at the beginning in mj-head, but let's start from the very beginning: the root element.

The document structure

MJML is not unlike HTML or XML. It is after all a markup language that tries to spit out HTML. As such, each MJML document begins with a simple root tag:

<mjml></mjml>

Inside this root element you have the same sections you're used to from a regular HTML document. One head and one body. They're just prefixed with mj. So the standard structure looks like so:

<mjml>
    <mj-head></mj-head>
    <mj-body></mj-body>
</mjml>

The mj-head is a pretty exciting place where you define all of your styling and meta data! It's probably the most fun part of writing an MJML-based email.

The document head

Now let's look at all the weird stuff in the mj-head and demystify it a bit.

<mj-head>
    <mj-font name="Montserrat" href="https://fonts.googleapis.com/css?family=Montserrat:400,700" />
    <mj-preview>Click the button inside to log into your account</mj-preview>

    <mj-attributes>
      <mj-text font-size="16px" line-height="24px" padding="0px 48px 16px" />
      <mj-button font-weight="bold" font-size="16px" background-color="#8e2de2" />
      <mj-divider border-color="#8e2de2" />
      <mj-all font-family="Montserrat, Helvetica, Arial, sans-serif" />
    </mj-attributes>

    <mj-style inline="inline">
      .link {
        color: #8e2de2;
        text-decoration: none
      }
    </mj-style>
</mj-head>

As the very first child of our document head we have a mj-font tag. It takes in a name and an href. If you look at where the href links to, you'll quickly figure out that all this does is load in a font hosted on Google Fonts. Of course, given the state of affairs, not all email clients support this. But some do and they get rewarded with prettier typography.

<mj-font name="Montserrat" href="https://fonts.googleapis.com/css?family=Montserrat:400,700" />


After this we find an mj-preview tag that contains some text. This is an invisible preview text that gets displayed in the inbox of some email clients right below the subject of the email. To give you some context, I'm talking about the light-gray text depicted here (in Apple Mail)

An example of preview text

You can use this to tease your subscribers about the content of your newsletter or give a summary of the contents. It's a bit of an art to find text that is just long enough to fill out the entire area, but not too long to be cut off by the email client.

<mj-preview>Click the button inside to log into your account</mj-preview>


Next up is a special one. mj-attributes allows you to set default styling for all MJML tags that you're using. You can of course override these at any time. These just exist so you don't have to repeat yourself over and over again.

Each child of the mj-attributes tag is another MJML tag such as mjml-text or mjml-button. Each of the CSS properties you want to use is defined as an attribute. This may be unusual at first, but you'll get used to it very quickly. There's also a special mj-all tag, which can be used to apply a style to all elements. (A bit like the CSS * selector)

Also note how I define some fallback fonts for the mj-all tag. This is so that email clients with bad CSS support still have at least a somewhat bearable font to look at.

<mj-attributes>
    <mj-all font-family="Montserrat, Helvetica, Arial, sans-serif" />
    <mj-text 
        font-size="16px"
        line-height="24px"
        padding="0px 48px 16px" 
    />
</mj-attributes>


And we wrap up our head component with an mj-style tag. You can use these to write regular CSS and media queries. One special thing to note here is that I added the inline="inline" attribute to my style tag. This makes MJML search for all elements with a class of link and adds a style attribute to it.

<!-- So this element -->
<a href="#" class="link">My awesome link!</a>

<!-- Turns into this -->
<a href="#" class="link" style="color: #8e2de2; text-decoration: none;">My awesome link!</a>

The reason why you would want this is once again rooted in the lack of CSS support in email clients. Many only support only basic selectors like a or h1, while Gmail just takes your style tag and throws it out entirely. Yup. That's actually a real thing that happens. Thanks Google.

The document body

This is where the meat of your email lives. MJML provides many custom tags to make building email as painless as can be. You can find a list of all tags available in the documentation.

I will highlight the ones I used below, starting with mj-section.

mj-section is meant to be used as a row in your layout. Imagine it a bit like Bootstraps or Foundations grid row elements. It offers some vertical spacing by default, but you can of course remove it. If you don't need any special thing like that you can of course also just use a div or table (and yes, I'm serious about the table).

mj-column allows you to define one or more columns in your layout. This way you can easily build a grid layout in your email. To have multiple columns next to each other put them into an mj-section and you're good to go.

An example of a 2x2 grid could look something like this

<mj-body>
    <mj-section>
        <mj-column>Row 1, Column 1</mj-column>
        <mj-column>Row 1, Column 2</mj-column>
    </mj-section>

    <mj-section>
        <mj-column>Row 2, Column 1</mj-column>
        <mj-column>Row 2, Column 2</mj-column>
    </mj-section>
</mj-body>


mj-image is an image. You probably knew that. By default MJML adds some styling to it to make it stand out a bit more, but once again, you can easily change that.

mj-text is a generic text container. You can put any p, h1 or whatever else in there and make it look pretty. If it's just a random blob of text you can also simply put that text in directly as a child.

mj-button is essentially a link that's styled like a button. Pass in href as an attribute and you've got a great call-to-action in less than 30 seconds. If that's not speedy email development I don't know what is.

mj-divider is a bit like an hr, except it's a lot easier to style. In my email template I used it to separate header and footer from the main content.

Styling tags

For each of the tags I named above, you can easily pass in attributes to give them a one-off style that you didn't add in your mj-attributes. For example I used this tag to style my footer text

<mj-text
    padding="8px 48px"
    font-size="11px"
    line-height="16px"
    color="#999999"
>
    Footer text
</mj-text>

If you end up using a set of styles more than once, but it is still not the default style you want, you can also define an mj-class that allows you to pass styles around like Oprah passes around cars.

You need only define the following in your mj-attributes

<mj-attributes>
    <mj-class name="padded" padding="20px" />
</mj-attributes>

Give you class a name and some CSS attributes you want it to use and off you go. Note however that this only works for MJML tags. For regular HTML tags you should make use of mj-style.

Summary

MJML is a great tool that helps you build responsive HTML email much faster than you would otherwise be able to. The predefined components help you get the most annoying parts out of the way so you can focus on the content and layout, instead of fiddling around with CSS hacks.

Of course, no tool is perfect and neither is MJML. So in the end, always be sure to test your email templates with a tool such as Litmus or EmailOnAcid. Just to be sure ;-)

Enjoy!~

Go to home page

You'll receive one email per released blog post and occasional updates when I release a new side project. That is all.