mjml-python
Compile MJML at runtime without a Node.js service, external API, or subprocess.
mjml-python is a Python wrapper around the Rust MJML engine MRML, a high-performance port of the official MJML.
Why
Using MJML traditionally requires either:
- running the Node.js-based MJML CLI as a subprocess, or
- calling an external MJML API service.
Both approaches add memory overhead, latency, operational complexity, and cost.
From MRML:
A Node.js server rendering an MJML template takes around 20 MB of RAM at startup and 130 MB under stress test. In Rust, less than 1.7 MB at startup and a bit less that 3 MB under stress test. The Rust version can also handle twice as many requests per second.
By embedding the Rust engine directly, mjml-python avoids all external dependencies while retaining full MJML compatibility.
Installation
Install from PyPI:
pip install mjml-python
Usage
Call mjml2html() with your MJML string:
from mjml import mjml2html
html = mjml2html(
'''
<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>
<!-- Say hello to the user -->
<mj-text font-size="20px" color="#F45E43" font-family="Open Sans">Hello World</mj-text>
</mj-column>
</mj-section>
<mj-section>
<mj-column>
<mj-social font-size="15px" icon-size="30px" mode="horizontal">
<mj-social-element name="facebook" href="https://mjml.io/">
Facebook
</mj-social-element>
<mj-social-element name="google" href="https://mjml.io/">
Google
</mj-social-element>
<mj-social-element name="twitter" href="https://mjml.io/">
Twitter
</mj-social-element>
</mj-social>
</mj-column>
</mj-section>
</mj-body>
</mjml>
''',
disable_comments=True,
social_icon_origin="https://example.com",
fonts={
"Open Sans": "https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,700",
"Ubuntu": "https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700",
}
)
Example using Django templates
from django.core.mail import send_mail
from django.template.loader import render_to_string
from mjml import mjml2html
context = {'foo': 'bar'}
text_message = render_to_string('my_text_template.txt', context)
html_message = mjml2html(render_to_string('my_mjml_template.mjml', context))
send_mail(
'Subject here',
text_message,
'from@example.com',
['to@example.com'],
fail_silently=False,
html_message=html_message,
)
Configuration Options
mjml-python supports the following options:
| Name | Type | Default value | Comment |
|---|---|---|---|
disable_comments |
bool |
False |
Strip comments out of rendered HTML |
social_icon_origin |
str | None |
None |
Custom URL origin for social icons. Icon name is appended (e.g. facebook.png). |
fonts |
dict[str, str] | None |
None |
Fonts imported in the HTML rendered by MJML. |
include_loader |
Callable[[str], str] | None |
None |
Fetch the included template using the path attribute. |
Default fonts (used when fonts=None):
{
"Open Sans": "https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,700",
"Droid Sans": "https://fonts.googleapis.com/css?family=Droid+Sans:300,400,500,700",
"Lato": "https://fonts.googleapis.com/css?family=Lato:300,400,500,700",
"Roboto": "https://fonts.googleapis.com/css?family=Roboto:300,400,500,700",
"Ubuntu": "https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700",
}
Why choose mjml-python instead of other MJML packages?
-
Simple, Pythonic API – a single
mjml2html()function that takes plain keyword arguments and returns a clean HTML string. No need to construct parser/render option objects or work with custom result classes. -
Batteries-included defaults – sensible defaults for fonts, comment handling, and include behavior, so most templates work with zero configuration.
-
Flexible include loader – pass a Python callable to handle
<mj-include>however you like (filesystem, database, HTTP, etc.) without learning MRML’s multiple loader types. -
Minimal surface area – intentionally focused on the common case: take MJML input → return production-ready HTML. No extra abstractions.
-
No external services or subprocesses required – rendering is performed entirely inside the Python extension using the embedded Rust MJML engine. No Node.js installation, MJML CLI, or HTTP API needed.
-
Stable wheel builds – lightweight ABI-3 wheels for all major platforms (CPython 3.8+), fast installation, and no compilation required on typical environments.
In short: mjml-python is ideal when you want a straightforward, Python-friendly way to render MJML without dealing with the full MRML configuration surface or external tooling.
Development
With Nix:
nix-shell
With Python 3.8+, uv, Rust 1.83+ and Cargo installed:
uv sync --locked --no-install-project
uv run --locked --no-sync maturin develop
uv run --locked --no-sync python -m unittest
Metadata
Release files for mjml-python 1.4.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mjml_python-1.4.2.tar.gz | 18.0 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| mjml_python-1.4.2-cp38-abi3-win_amd64.whl | CPython 3.8 | abi3 | Windows x86-64 | Details |
| mjml_python-1.4.2-cp38-abi3-musllinux_1_2_x86_64.whl | CPython 3.8 | abi3 | Linux musl 1.2+ x86-64 | Details |
| mjml_python-1.4.2-cp38-abi3-musllinux_1_2_aarch64.whl | CPython 3.8 | abi3 | Linux musl 1.2+ ARM64 | Details |
| mjml_python-1.4.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.8 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| mjml_python-1.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.8 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| mjml_python-1.4.2-cp38-abi3-macosx_11_0_arm64.whl | CPython 3.8 | abi3 | macOS 11.0+ ARM64 | Details |
| mjml_python-1.4.2-cp38-abi3-macosx_10_12_x86_64.whl | CPython 3.8 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 4.3 MB
Release files / mjml_python-1.4.2.tar.gz
| Download URL | mjml_python-1.4.2.tar.gz |
|---|---|
| Size | 18.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a3b10d0325653e85a7d2c4180ebc521141bedc0944c9a334d075151500b252b5
|
|
BLAKE2b-256 checksum How to use checksums |
d95020c4f6d8ce095317b3bad5e287ba2131e9e43fb8db1a2744b5a3bc5d1119
|
| 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 1, 2026.
Transparency logRelease files / mjml_python-1.4.2-cp38-abi3-win_amd64.whl
| Download URL | mjml_python-1.4.2-cp38-abi3-win_amd64.whl |
|---|---|
| Size | 451.0 kB |
| Tags | CPython 3.8 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
e4df81e22e5c9d93a9b799fd7eb2a9f1eba5edbc02efb43804de5a2a50d683e0
|
|
BLAKE2b-256 checksum How to use checksums |
2ac7518db6d835daaeb85608714a35cb738dd1a4e871b2b6d840a0f6c6c9bf76
|
| 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 1, 2026.
Transparency logRelease files / mjml_python-1.4.2-cp38-abi3-musllinux_1_2_x86_64.whl
| Download URL | mjml_python-1.4.2-cp38-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 701.4 kB |
| Tags | CPython 3.8 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
e3cb288724e89125a9735e367345d01bdf4d1ac57e973650cb9c7929885fb7d3
|
|
BLAKE2b-256 checksum How to use checksums |
3891b5e3606fbc61a0c05c3c9aa7ab3a7e2c8c2c79800c3179a154458cfd2611
|
| 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 1, 2026.
Transparency logRelease files / mjml_python-1.4.2-cp38-abi3-musllinux_1_2_aarch64.whl
| Download URL | mjml_python-1.4.2-cp38-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 793.4 kB |
| Tags | CPython 3.8 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
355bedef9e3d858425ca4664cd8f16849341db6abc15b351a846f12ec687db27
|
|
BLAKE2b-256 checksum How to use checksums |
75c4d5e398455bc1295d35674c848391f0c461bb8dcb65d83b307f5d4bcd1afd
|
| 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 1, 2026.
Transparency logRelease files / mjml_python-1.4.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | mjml_python-1.4.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 611.7 kB |
| Tags | CPython 3.8 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
6d05b2b86679e6539b6eb63b027a56870af2e08b63c67bdddaaff2d532e6088d
|
|
BLAKE2b-256 checksum How to use checksums |
ec41336a9a2d112662b4ded8d135573feda15fa67e2307fc8e8591be7b0ee939
|
| 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 1, 2026.
Transparency logRelease files / mjml_python-1.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | mjml_python-1.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 616.8 kB |
| Tags | CPython 3.8 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
0e91e925a2fc6324dc39fe7b500166d3480e70e00f13b2c3bb78b1953f887c45
|
|
BLAKE2b-256 checksum How to use checksums |
87337d2fd059e99e38f813ea3682c5aa379dfe3c6587659c103a81e195964351
|
| 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 1, 2026.
Transparency logRelease files / mjml_python-1.4.2-cp38-abi3-macosx_11_0_arm64.whl
| Download URL | mjml_python-1.4.2-cp38-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 548.8 kB |
| Tags | CPython 3.8 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
0a79b021110ea36b5f86a57c3f95a38f0b94f32b8c5fc421b672783c7fc8e06c
|
|
BLAKE2b-256 checksum How to use checksums |
4b7009a519382e58aae8894c0213210339d5d5649f52db84ba499c99fb90dab3
|
| 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 1, 2026.
Transparency logRelease files / mjml_python-1.4.2-cp38-abi3-macosx_10_12_x86_64.whl
| Download URL | mjml_python-1.4.2-cp38-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 551.4 kB |
| Tags | CPython 3.8 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
29342a947af5a578cf30ed1802726e5ed71e7226a13af6b16030164ad836fde5
|
|
BLAKE2b-256 checksum How to use checksums |
6115e05e37b710b874b905afc0fa1f31e31c0262450a3f24620e93fa6d50a286
|
| 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 1, 2026.
Transparency log