Skip to main content

django-mjml-templates

A drop-in replacement for Django's template engine that also understands .mjml files, so an email template is an ordinary Django template that happens to compile to email HTML. MJML is compiled with mjml-python, the Rust mrml port, so there is no Node dependency; the plain-text fallback uses html2text.

Setup

uv add django-mjml-templates   # or pip install

Then swap the backend; everything else about TEMPLATES stays as it was. The engine is still named django (Django takes the default name from the module segment of BACKEND), so engines["django"] and using="django" keep working.

TEMPLATES = [{
    "BACKEND": "mjml_templates.django.DjangoTemplates",   # was django.template.backends.django.DjangoTemplates
    "APP_DIRS": True,
    "OPTIONS": {"context_processors": [...]},
}]

Every other template renders exactly as before. Only a template whose name ends in .mjml — or a from_string whose code starts with <mjml — is treated as email.

Writing an email

An .mjml file is a Django template first: {% extends %}, loops, {% url %} and autoescaping all run as usual, and the MJML that produces is compiled on render. See the MJML docs for the components themselves.

{# templates/email/welcome.mjml #}
<mjml>
  <mj-head><mj-title>Welcome, {{ user.first_name }}</mj-title></mj-head>
  <mj-body>
    <mj-section><mj-column>
      <mj-text>Hi {{ user.first_name }}, thanks for signing up.</mj-text>
      <mj-button href="{% url 'start' %}">Get started</mj-button>
    </mj-column></mj-section>
  </mj-body>
</mjml>

<mj-title> doubles as the subject line. A sibling email/welcome.txt, if you write one, becomes the plain-text body; without it the HTML is run through html2text, with MJML's layout tables unwrapped first (an <mj-table> stays a table) and each button written as Label: url.

Sending

from django.template.loader import get_template

get_template("email/welcome.mjml").render_email({"user": user}, request, to=[user.email]).send()

render_email(context, request=None, *, subject="", **kwargs) returns an unsent EmailMultiAlternatives with both bodies attached, so you can still attach() a file first. kwargs go straight to EmailMultiAlternatives: to, from_email, bcc, reply_to, headers, and so on.

render_email_parts(context, request=None, *, subject="") gives the raw (subject, text, html) named tuple instead. Both methods exist on .mjml templates only. The subject is the one you pass, else the rendered <mj-title>, else a ValueError.

Absolute URLs

A relative link is dead in an email — there is no page for the mail client to resolve it against. So every .mjml render rewrites href, src, background and CSS url() against a base URL, and {% url %}, {% static %} and hand-written paths all come out absolute. Anything already carrying a scheme, a protocol-relative //host/… or a bare #fragment is left alone.

The base comes from the request if you pass one, and otherwise from MJML_BASE_URL:

MJML_BASE_URL = "https://example.com"   # a full URL, used as-is
MJML_BASE_URL = "https://"              # scheme only (the default) — paired with the current Site

The default means projects with django.contrib.sites installed need no setting at all. With no request, no full URL and no sites app there is no base to work from, and URLs are left relative rather than raising — so sending from a Celery task or a management command wants one of the two. (A MJML_BASE_URL with no scheme at all is an ImproperlyConfigured.)

The rewrite only reaches URLs in markup. Where a template writes one out as text, and in the .txt sibling where there is no markup at all, use mjml_base:

{# email/welcome.txt #}
Sign up: {{ mjml_base }}{% url "signup" %}

Pass your own mjml_base in the context and it wins over the derived one.

Metadata

Release files for django-mjml-templates 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-mjml-templates 1.2.0
File Size Uploaded
django_mjml_templates-1.2.0.tar.gz 28.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-mjml-templates 1.2.0
File Interpreter ABI Platform
django_mjml_templates-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 36.9 kB

Release files / django_mjml_templates-1.2.0.tar.gz

Download URL django_mjml_templates-1.2.0.tar.gz
Size 28.1 kB
Tags Source
SHA-256 checksum
How to use checksums
7e37bd422552db12687fd40f2e8d0ece0af851abd141b8c799d2f9ac163376d6
BLAKE2b-256 checksum
How to use checksums
9d8f4fe7343d0118e71c8954a08fffdb73b8376eb953007d3db3c436511132c2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release files / django_mjml_templates-1.2.0-py3-none-any.whl

Download URL django_mjml_templates-1.2.0-py3-none-any.whl
Size 8.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8d6f2bcc99fd9cbc4d6ad68f313b5ad208937449c56a4a5f5f9935aca348bbb3
BLAKE2b-256 checksum
How to use checksums
efe6260b3a6e0a97a2e5f38e39e55b2670dfd0eea506b59150f05c9ff042d170
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release history Release notifications | RSS feed

1.3.0

2 release files

This release

1.2.0 This release

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page