back arrow back

Diving Deeper Into MJML

Cover Image for Diving Deeper Into MJML

This was originally posted on Edenspiekermanm's Developer's Blog.

Last year, our colleague Hugo Giraudel wrote a blog post about integrating MJML into Rails. After working with it for a while in some of our other projects and with the recent upgrade of the language to version 3, we thought that it's time for an update!

Use MJML in a Rails Email Layout Setup

If you have different kinds of emails in your project, you might want to extract some of the shared logic into something you could reuse ("Don't Repeat Yourself!"). In Rails this is done through the Action Mailer Layouts. To do this you have to create a layout file and use yield to render your partial inside the layout:

<!-- ./app/views/layouts/mjml_email.mjml -->

<mjml>
  <mj-body>
    <mj-container>
      <%= yield %>
    </mj-container>
  </mj-body>
</mjml>

Now you can add this layout to your mailer class:

# ./app/mailers/user_mailer.rb

class UserMailer < ApplcationMailer::Base
  layout: 'mjml_email' # <- add this

  def welcome_email(user)
    @name = user.name

    mail(to: user.email) do |format|
      format.mjml
    end
  end
end

All you have to do now is to remove the shared markups from your partial:

<!-- ./app/views/user_mailer/welcome_email.mjml.erb -->

<mj-section>
  <mj-column>
    <mj-text> Hello <%= @name %></mj-text>
  </mj-column>
</mj-section>

Pay attention to the different file formats (when and where to use .mjml or .mjml.erb), otherwise it won't work!

Style Your MJML Templates

Creating email templates for the same project also means applying similar stylings to each of the templates. With the recent upgrade of MJML to version 3 it became a lot easier to reuse some of the stylings.

Within the mj-attributes tag you can now define global stylings with mj-all; create styling classes with mj-class; or set the styling for a specific tag by just mentioning it:

<!-- ./app/views/layouts/mjml_email.mjml -->

<mjml>
  <mj-head>
    <mj-attributes>
      <mj-all font-family="Arial" align="center"/>
      <mj-class name="big" font-size="42px" />
      <mj-section background-color="#ffff00"/>
    <mj-attributes>
  </mj-head>

  <mj-body>
    <mj-container>
      <mj-section>
        <mj-text mj-class="big">
          <!-- This text will appear
                * centered
                * in Arial
                * with a font-size of 42px
                * on a bright yellow background
                * 😎
          -->
          Hello World!
        </mj-text>
      </mj-section>
    </mj-container>
  </mj-body>
</mjml>

Another way to apply styling is to use mj-style and write plain CSS in it. You can then use mj-raw to write plain html and apply the CSS class:

<!-- ./app/views/layouts/mjml_email.mjml -->

<mjml>
  <mj-head>
    <mj-style>
      .card-shadow {
        margin-top: 1.6em;
        -webkit-box-shadow: 0px 0px 15px 5px rgba(220,220,220,0.5);
        -moz-box-shadow: 0px 0px 15px 5px rgba(220,220,220,0.5);
        box-shadow: 0px 0px 15px 5px rgba(220,220,220,0.5);
      }
    </mj-style>
  </mj-head>

  <mj-body>
    <mj-container>
      <mj-section>
        <mj-raw>
          <!-- This adds some box-shadow to the image -->
          <img class="card-shadow" src="http://example.com/image.jpg"/>
        </mj-raw>
      </mj-section>
    </mj-container>
  </mj-body>
</mjml>

Use Web Fonts

There are several ways of integrating web fonts into your emails. Campaign Monitor has a good introduction on that. MJML introduced the mj-font tag to allow customized fonts, which should be sufficient in most of the cases:

<mjml>
  <mj-head>
    <mj-font name="Oswald" href="https://fonts.googleapis.com/css?family=Oswald" />
  </mj-head>

  <mj-body>
    <mj-container>
      <mj-section>
        <mj-column>
          <mj-text font-family="Oswald, Arial">
            Hello World!
          </mj-text>
        </mj-column>
      </mj-section>
    </mj-container>
  </mj-body>
</mjml>

But in one of our projects, we are loading fonts which are nested in the Rails assets folder structure ./app/assets/fonts/* and we used to load it with @font-face into our email templates.

To allow this again we had to hack MJML a bit, since we weren't able to find anything else to replace this.

<mjml>
  <mj-head>
    <!-- mj-font gets converted into @import -->
    <mj-font name="Oswald" href="https://fonts.googleapis.com/css?family=Oswald" />
    <mj-attributes>
      <!-- This sets the global font of the template to FSBlakeLight and adds fallbacks. -->
      <mj-all font-family="FSBlakeLight, Oswald, Arial"/>
    </mj-attributes>
  </mj-head>

  <mj-body>
    <mj-container>
      <mj-raw>
        <!--
          This is a bit of a hack!
          mj-raw only works within the mj-container tag.
        -->
        <style>
          @font-face {
            font-family: 'FSBlakeLight';
            src: url("<%= asset_path('blake_light/fs_blake_web-light.eot') %>");
            src: url("<%= asset_path('blake_light/fs_blake_web-light.woff') %>") format('woff'),
                 url("<%= asset_path('blake_light/fs_blake_web-light.ttf') %>") format('truetype'),
                 url("<%= asset_path('blake_light/fs_blake_web-light.svg#FSBlake-Light') %>") format('svg');
            font-weight: normal;
            font-style: normal;
          }
        </style>
      </mj-raw>
      <%= yield %>
    </mj-container>
  </mj-body>
</mjml>

Wrapping Up

We are still very happy with MJML and are trying to integrate it into more and more of our projects.

The version 3 upgrade helps a lot in terms of DRY up the code and to integrate standard styling elements. But when the design becomes more complex, we did reach some of the limits of MJML. But the lively discussions and issue requests online promise that new features are soon to come.

We've been testing our emails with Litmus, a service which allows us to see how our emails get rendered on different devices with different email clients. Since the switch to MJML we've more or less stopped experiencing broken stylings or missing elements. 🤘

PS.: the official list of MJML packages also has been a great source of help.