If you’re a Django developer tired of wrestling with HTML email templates that don’t work in Gmail/Outlook/Thunderbird/Apple Mail/…., MJML (Mail Jet Markup Language) MIGHT be the solution you’ve been looking for. MJML simplifies the process of creating responsive emails that look great and are consistent across all devices and email clients.

What is MJML?

MJML is an open-source markup language created by Mailjet, a leading email service provider. It’s designed to “reduce” the pain of coding responsive emails by abstracting complex HTML and CSS into a simpler, more “intuitive” syntax… Maybe.

Why Use MJML with Django?

  • Responsive by default: MJML ensures your emails look great on all devices.
  • Simplifies email design: Write less code and achieve better results.
  • Integrates well with Django: Like everything Django…

Setting Up MJML in Your Django Project

Assuming you already have a Django project set up and email working, let’s integrate MJML:

1. First, install the django-mjml package:

pip install django-mjml

2. Add ‘mjml’ to your INSTALLED_APPS in settings.py:

INSTALLED_APPS = [
    ...
    'mjml',
    ...
]

3. Configure MJML in your settings.py:

MJML_BACKEND = 'mjml.backends.cmd.CommandBackend'

This setup assumes you the MJML CLI installed. For alternative backends, check the django-mjml documentation. It can also be done by API calls too if you have “nothing to hide”, but I value my users privacy too much.

But why ? Because you can’t send plain MJML emails, it’s an abstraction layer to write your emails quickly and then convert them to HTML. You other option would be generating the HTML and skipping all the Django part, but that would be a very short blog post.

Creating Your First MJML Template

Now that MJML is set up, let’s create a simple email template. Create a new file welcome_email.mjml in your templates directory:

{% mjml %}
<mjml>
  <mj-body>
    <mj-section>
      <mj-column>
        <mj-text>
          Welcome to our Django site, {{ user.username }}!
        </mj-text>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>
{% endmjml %}

This simple template demonstrates how MJML components (<mj-section>, <mj-column>, <mj-text>) are used to structure your email. The {% mjml %} tags tell Django to process this as MJML.

Here is a good repo for HTML “Battle tested” email templates if MJML doesn’t seem appropriate for you.

For more advanced MJML usage and components, refer to the official MJML documentation.

Using MJML in Your Django Views

To use your MJML template in a view, you can render it like any other Django template:

from django.core.mail import send_mail
from django.template.loader import render_to_string

def send_welcome_email(user):
    html_message = render_to_string('welcome_email.mjml', {'user': user})
    send_mail(
        'Welcome!',
        'Welcome to our site!',
        '[email protected]',
        [user.email],
        html_message=html_message
    )

That’s it! You’ve now integrated MJML into your Django project, allowing you to create responsive emails with ease. As you explore MJML further, you’ll find it significantly simplifies the process of creating complex email layouts while ensuring compatibility across different email clients.

Remember, while MJML handles much of the heavy lifting in terms of email responsiveness, it’s always a good idea to test your emails across different devices and clients to ensure optimal display.

Would you like to know more about MJML ?


Bonus : Advanced example

Here’s a working example of an email sent on a new blogpost. It’s not super good looking because … I made it, sorry 🙂

<mjml>
  <mj-head>
    <mj-font name="Roboto" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" />
    <mj-attributes>
      <mj-text font-family="Roboto" font-size="16px" color="#555" line-height="24px" />
      <mj-section padding="0px" />
      <mj-class name="title" font-size="30px" font-weight="bold" color="#333" />
      <mj-class name="subtitle" font-size="20px" font-weight="300" color="#888" />
      <mj-class name="content" font-size="16px" line-height="1.5" />
      <mj-class name="button" background-color="#4CAF50" color="white" padding="15px 30px" border-radius="50px" text-transform="uppercase" font-weight="bold" />
    </mj-attributes>
  </mj-head>

  <mj-body background-color="#F0F0F0">
    <mj-section background-color="#4CAF50" padding="20px 0">
      <mj-column>
        <mj-text align="center" color="white" font-size="33px" font-family="Roboto" font-weight="700">
          My Django Blog
        </mj-text>
      </mj-column>
    </mj-section>

    <mj-section background-color="white" padding="0px">
      <mj-column>
        <mj-image
          src="https://via.placeholder.com/600x300"
          alt="Featured Blog Image"
          width="600px"
        />
      </mj-column>
    </mj-section>

    <mj-section background-color="white" padding="20px">
      <mj-column>
        <mj-text mj-class="title" align="center">
          MJML for Dummies
        </mj-text>
      </mj-column>
    </mj-section>


    <mj-section background-color="white" padding="20px 40px">
      <mj-column>
        <mj-text mj-class="content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse non purus nec lorem dapibus vehicula. 
        </mj-text>
      </mj-column>
    </mj-section>

    <mj-section background-color="white" padding="20px 40px">
      <mj-column>
        <mj-button mj-class="button" href="https://tech.gillyb.net" align="center">
          Read Full Post
        </mj-button>
      </mj-column>
    </mj-section>

    <mj-section background-color="white" padding="0 20px">
      <mj-column>
        <mj-divider border-color="#E0E0E0" />
      </mj-column>
    </mj-section>

    <mj-section background-color="#4CAF50" padding="20px 0">
      <mj-column>
        <mj-text align="center" color="white" font-size="14px" >
          © 2024 Gilly B. Tech Blog - All Rights Reserved
        </mj-text>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>

By Romain

Leave a Reply

Your email address will not be published. Required fields are marked *