Oh Dear is the all-in-one monitoring tool for your entire website. We monitor uptime, SSL certificates, broken links, scheduled tasks and more. You'll get a notifications for us when something's wrong. All that paired with a developer friendly API and kick-ass documentation. O, and you'll also be able to create a public status page under a minute. Start monitoring using our free trial now.

Craft emails that look good in each email client using MJML

Original – by Freek Van der Herten – 5 minute read

In a perfect world, email clients can render HTML as good as major browsers. Unfortunately, this is not the case. Email clients don't support modern HTML and CSS niceties and have a lot of quirks to be mindful of. Making sure an HTML email looks good in the most used email clients takes a lot of work.

To make crafting HTML emails a lot more enjoyable, the folks at Mailjet created a solution called MJML, which stands for "Mailjet Markup Language." It's an easy-to-use abstraction layer over HTML.

We have created a new package called spatie/mjml-php to easily convert MJML to HTML using PHP. If you're using Sidecar, you'll be happy to know that we've also created a package called spatie/mjml-sidecar, to convert MJML to HTML using Sidecar.

In this blog post, I'd like to introduce the package to you.

Taking a look at MJML

MJML looks very much like HTML but with mj tags. Let's take a look at some valid MJML.

<mjml>
  <mj-body>
    <mj-section>
      <mj-column>
        <mj-text invalid-attribute>Hello World</mj-text>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>

In the code above, you can see that there are many mj components that you can make use of. I won't explain them all as they are described in the extensive documentation. In those docs, you'll also find a complete example of an entire mail.

When converting the MJML to HTML, this is the result:

<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">

<head>
  <title></title>
  <!--[if !mso]><!-->
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <!--<![endif]-->
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style type="text/css">
    #outlook a {
      padding: 0;
    }

    body {
      margin: 0;
      padding: 0;
      -webkit-text-size-adjust: 100%;
      -ms-text-size-adjust: 100%;
    }

    table,
    td {
      border-collapse: collapse;
      mso-table-space: 0pt;
      mso-table-space: 0pt;
    }

    img {
      border: 0;
      height: auto;
      line-height: 100%;
      outline: none;
      text-decoration: none;
      -ms-interpolation-mode: bicubic;
    }

    p {
      display: block;
      margin: 13px 0;
    }
  </style>
  <!--[if mso]>
    <noscript>
    <xml>
    <o:OfficeDocumentSettings>
      <o:AllowPNG/>
      <o:PixelsPerInch>96</o:PixelsPerInch>
    </o:OfficeDocumentSettings>
    </xml>
    </noscript>
    <![endif]-->
  <!--[if lte mso 11]>
    <style type="text/css">
      .mj-outlook-group-fix { width:100% !important; }
    </style>
    <![endif]-->
  <!--[if !mso]><!-->
  <link href="https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700" rel="stylesheet" type="text/css">
  <style type="text/css">
    @import url(https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700);
  </style>
  <!--<![endif]-->
  <style type="text/css">
    @media only screen and (min-width:480px) {
      .mj-column-per-100 {
        width: 100% !important;
        max-width: 100%;
      }
    }
  </style>
  <style media="screen and (min-width:480px)">
    .moz-text-html .mj-column-per-100 {
      width: 100% !important;
      max-width: 100%;
    }
  </style>
  <style type="text/css">
  </style>
  <style type="text/css">
  </style>
</head>

<body style="word-spacing:normal;">
  <div style="">
    <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
    <div style="margin:0px auto;max-width:600px;">
      <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
        <tbody>
          <tr>
            <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
              <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
              <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
                <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
                  <tbody>
                    <tr>
                      <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
                        <div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;line-height:1;text-align:left;color:#000000;">Hello World</div>
                      </td>
                    </tr>
                  </tbody>
                </table>
              </div>
              <!--[if mso | IE]></td></tr></table><![endif]-->
            </td>
          </tr>
        </tbody>
      </table>
    </div>
    <!--[if mso | IE]></td></tr></table><![endif]-->
  </div>
</body>
</html>

Wow, that's a lot of HTML you didn't have to come up with yourself. Neat! As you can see above, some code is being added to ensure it renders well in older email clients.

Converting MJML to HTML

The MJML provides many different options to convert MJML to HTML: there is a Node tool, a CLI tool, and also a website where you can convert MJML manually. Using one of the many plugins, you can also have auto-completion for MJML in your favorite editor.

Our newest package, spatie/mjml-php, can convert MJML to HTML using PHP. It's a handcrafted, easy-to-use wrapper around the Node tool. By using the Node tool under the hood, we can just make use of all functionality there without having to implement it all in PHP. But as a user of the PHP package, you don't need to mind all this.

The package can be installed using composer:

composer require spatie/mjml-php

Also, you should make sure the Node MJML package is available in your project.

npm install mjml

With this out of the way, you can start converting MJML.

use Spatie\Mjml\Mjml;

// let's assume $mjml contains the MJML you want to convert

$html = Mjml::new()->toHtml($mjml);

There are a couple of methods, such as beautify, hideComments(), minify() to customize the conversion process.

use Spatie\Mjml\Mjml;

// let's assume $mjml contains the MJML you want to convert
$minifiedHtml = Mjml::new()->minify()->toHtml($mjml);

We also added a method that you can use to ensure the given MJML is valid.

use Spatie\Mjml\Mjml;

Mjml::new()->canConvert($mjml); // returns a boolean

Integration with Sidecar

Sidecar is a fantastic package that easily lets you use other programming languages besides PHP in your Laravel codebase. Under the hood, this work by executing code in another language on AWS Lambda.

We've created a package spatie/mjml-sidecar to convert MJML to HTML using Sidecar. This way, the conversion process will be done on AWS, and you don't have to have Node available on your server.

With the sidecar package installed, you should have to tack on the sidecar() method to execute the conversion on AWS.

use Spatie\Mjml\Mjml;

// let's assume $mjml contains the MJML you want to convert

$html = Mjml::new()->sidecar()->toHtml($mjml);

Why we built this

At Spatie, we're always building packages not only because we like doing it but because we need them ourselves. The MJML package will be used in our paid product Mailcoach. Mailcoach is an affordable, developer- and privacy-friendly solution to send newsletters, set up email automation, and send transactional emails.

Using Mailcoach, you could already create your templates and emails using Markdown. The spatie/mjml-php package will be used to add MJML support to Mailcoach. We bet that many of our users will be happy that using MJML will make it easier to create emails that will look good to each email client.

MJML support will be added in the upcoming v7 of Mailcoach in both the hosted and self-hosted versions. We expect to ship this release in the next couple of weeks.

In closing

There are a couple of options available on our MJML package that we didn't cover in our blog post. To learn more, head over to the readme of spatie/mjml-php on GitHub.

This is one of many packages that we've made. You'll find an extensive list of Laravel and PHP packages we released previously on our company website. There's probably something there for your next project. If you like our open-source stuff, be sure to also look at our paid products that make working on open-source sustainable for our company.

Stay up to date with all things Laravel, PHP, and JavaScript.

You can follow me on these platforms:

On all these platforms, regularly share programming tips, and what I myself have learned in ongoing projects.

Every month I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.

Comments

Henk-Reijer avatar

Maybe also an interesting link for you guys:

https://www.mailbites.io

some kind of blocks to create mjml mails that can be converted to html.

Ahmed Ammar avatar

Looks good. Thanks for sharing!

Comments powered by Laravel Comments
Want to join the conversation? Log in or create an account to post a comment.