Introduction
In this tutorial, I will talk about how to develop Responsive HTML Email in Django.
After reading this article, you will learn:
- What is responsive HTML Email and the benefits
- What is MJML and How to develop responsive HTML Email with MJML
- How to make MJML work with Django template.
- How to check HTML Email in local development environment.
What Is Responsive HTML Email
With responsive design, you can send email templates which can change depending on what screen size they are viewed. These emails will always render correctly regardless of the device it’s viewed on.
Responsive email designs use CSS media queries to produce two different copies that depend on the size of your user’s screen. Media queries will automatically adjust the email copy’s layout, content, and text size to the user’s device screen.
If you have experience with some responsive frameworks such as Bootstrap
, Tailwind CSS
, I guess you already know what Responsive HTML Email is.
HTML Email Can Be a Pain
If you have developed HTML email, I am sure the experience was not good:
- HTML emails lack standards: The code used to design these emails is just plain HTML and CSS which is no way like on the web. No real standards exist between email clients, leading to a crazy code.
- Email Clients: Email clients, like Outlook and Gmail, all render HTML and CSS differently. So the same message looks different on different clients. It becomes difficult to design and create one email that looks perfect on all clients.
- Lots of hacks: Even well-designed email campaigns need to rely on client-specific hacks to make things work.
- No JavaScript: The web’s favorite language has no place in email, as email clients (rightly) strip it due to security concerns.
- Inline styles: Unfortunately, most email clients force you to rely on inline styles and attributes for nearly everything in email.
If we check HTML email template provided by Mailgun https://github.com/mailgun/transactional-email-templates, we can see HTML code like this:
As you can see, the HTML is not clean and not easy to maintain.
Are there other solutions which can help us solve this problem?
MJML
MJML stands for "Mailjet Markup Language". MJML has been designed to reduce the pain of coding a responsive email. Leveraging its semantic syntax and a rich standard components library, making your email responsive is not an issue anymore.
Below is an example
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-image width="100px" src="/assets/img/logo-small.png"></mj-image>
<mj-divider border-color="#F45E43"></mj-divider>
<mj-text font-size="20px" color="#F45E43" font-family="helvetica">Hello World</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
- The syntax is very similar with HTML
- You can set
attributes
as you like to change the style.
- You can check the great tutorial https://mjml.io/getting-started/1
- And an online editor is available for you to play with MJML and check the real-time result https://mjml.io/try-it-live
- And you can also check the complete guide https://documentation.mjml.io/
With MJML, you can quickly build good looking responsive HTML email, there are also many MJML Responsive Email Templates for you to learn and use.
Develop Workflow
- We can write MJML code in the Django template.
- When rendering the Django template, convert the MJML code to the final HTML code, and use the HTML sending Email.
How to send MJML in Django
django-mjml can help us get the work done in elegant way.
Create Django Project
$ mkdir django_email_project && cd django_email_project
$ python3.10 -m venv venv
$ source venv/bin/activate
(venv)$
(venv)$ pip install django==4.1.1
(venv)$ django-admin startproject django_project .
(venv)$ python manage.py migrate
(venv)$ python manage.py runserver
Now check on http://127.0.0.1:8000/
and you will see Django welcome page, then press Ctrl+C
to terminate the dev server.
Install django-mjml
(venv)$ pip install django-mjml
Update django_project/settings.py
INSTALLED_APPS = (
...,
'mjml',
)
MJML_BACKEND_MODE = "cmd"
MJML_EXEC_CMD = "node_modules/.bin/mjml"
Notes:
django-mjml
supports different modes, here we use the simple CMD mode, thedjango-mjml
will run CMD command to convert the MJML code to HTML- The
MJML
command will be installed viaNodejs
Next, we will install MJML
command:
$ npm install mjml
We will see a package.json created under the root directory
{
"dependencies": {
"mjml": "^4.13.0"
}
}
Let's test
$ ./node_modules/.bin/mjml --help
Options:
-r, --read Compile MJML File(s) [array]
-m, --migrate Migrate MJML3 File(s) (deprecated) [array]
-v, --validate Run validator on File(s) [array]
-w, --watch Watch and compile MJML File(s) when modified[array]
-i, --stdin Compiles MJML from input stream
-s, --stdout Output HTML to stdout
-o, --output Filename/Directory to output compiled files[string]
-c, --config Option to pass to mjml-core
-V, --version Show version number [boolean]
--noStdoutFileComment Add no file comment to stdout [boolean]
--help Show help [boolean]
Now MJML
command has been installed successfully
Templates
$ mkdir django_project/templates
Update TEMPLATES
in django_project/settings.py, so Django can know where to find the templates
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['django_project/templates'], # update
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Create django_project/templates/email-templates.html
{% load mjml %}
{% mjml %}
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text font-size="20px" color="#F45E43" font-family="helvetica">Hello World</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
{% endmjml %}
Test
MailHog is Web and API based SMTP testing
We can use MailHog
to help us check Email on local.
$ docker run --rm -d -p 1025:1025 -p 8025:8025 mailhog/mailhog
1025
is the SMTP port8025
is the Mailhog web dashboard port
Update django_project/settings.py, so the Django will send email to localhost:1025
, which mailhog
is serving on that port.
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_PORT = 1025
Run code below in Django shell to test
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
html_body = render_to_string("email-templates.html")
message = EmailMultiAlternatives(
subject='Django HTML Email',
body="mail testing",
from_email='[email protected]',
to=['[email protected]']
)
message.attach_alternative(html_body, "text/html")
message.send(fail_silently=False)
Notes:
- If you check
html_body
in the Django shell, you can see it is already been converted fromMJML
toHTML
We can also check the HTML email on Mailhog dashboard http://localhost:8025/
As you can see, MJML
can help us develop responsive HTML email in elegant way.
Use Case
When you build Django project, you might develop user Signup/Login feature with the great django-allauth project.
django-allauth
supports HTML email template but you need to add the templates on your own.
With django-mjml
, you can write MJML in Django template, while you can still use Django template syntax.
For example, you can create account/email/email_confirmation_message.html
in the templates
directory, to improve the style of the Confirm Email Address
Tips
django-mjml
supports different modes of deployment. For example, you can launch a standalone server which focus on convertingMJML
toHTML
- For simplicity,
mjml
cmd installed undernode_modules
is enough in most cases.