django-mail-preview
See every email your Django app sends, rendered in the browser, without sending a single one.
Checking email in a Django project usually means reading raw MIME in the console or
sending real mail to a test inbox. django-mail-preview gives you one page inside the
project instead: a capture email backend stores the mail your app sends, and preview
classes build emails from sample data without sending them. The page answers 404
unless DEBUG is on, needs no staticfiles, and adds no models or migrations. Django
is the only dependency.
Quickstart
python -m pip install django-mail-preview
Add the app and the capture backend to your settings:
INSTALLED_APPS = [
# ...
"django_mail_preview",
]
# Django 6.1 and later
MAILERS = {
"default": {"BACKEND": "django_mail_preview.backends.EmailBackend"},
}
# Django 5.2 and 6.0
EMAIL_BACKEND = "django_mail_preview.backends.EmailBackend"
Include the URLs:
from django.urls import include, path
urlpatterns = [
# ...
path("__mail-preview__/", include("django_mail_preview.urls")),
]
Open http://localhost:8000/__mail-preview__/. Every email the app sends from now on
appears there, rendered in a sandboxed frame, with its plain text, source, headers and
attachments a tab away. The include needs no if settings.DEBUG wrapper, because the
views answer 404 unless DEBUG is on.
Previews
To see an email without triggering it, add a previews.py to any app. Each public
method of an EmailPreview subclass builds one email from sample data:
# accounts/previews.py
from django_mail_preview import EmailPreview
from .emails import password_reset_email, welcome_email
from .models import User
class AccountEmails(EmailPreview):
def password_reset(self):
"""Sent after the "forgot password" form."""
user = User(first_name="Ada", email="ada@example.com")
return password_reset_email(user)
def welcome(self):
return welcome_email(User(first_name="Ada"))
Refresh the page, and the sidebar lists password_reset and welcome under
accounts. Previews are found by convention, rebuilt on every request and never
imported in production. A query string on a preview's URL reaches it as
self.params, so one method can show variants. One parametrised test renders every
preview:
import pytest
from django_mail_preview import get_previews
@pytest.mark.parametrize("preview", get_previews(), ids=lambda preview: preview.id)
def test_preview_renders(preview):
preview.render()
Compatibility
| Python | Django |
|---|---|
| 3.10, 3.11 | 5.2 |
| 3.12, 3.13, 3.14 | 5.2, 6.0, 6.1 |
Security
The pages answer 404 unless DEBUG is True, and MAIL_PREVIEW_ALLOW replaces that
rule with your own check for a staging server. Email HTML renders in a sandboxed frame
under a Content Security Policy that lets images and styles load but never runs a
script, and every attachment downloads instead of rendering in your site's origin.
Documentation
The full documentation is at django-mail-preview.readthedocs.io:
License
MIT.
Metadata
Release files for django-mail-preview 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_mail_preview-0.1.0.tar.gz | 56.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_mail_preview-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 96.5 kB
Release files / django_mail_preview-0.1.0.tar.gz
| Download URL | django_mail_preview-0.1.0.tar.gz |
|---|---|
| Size | 56.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2133fdd737670568f65627f42f569bed47f166154dcbe18f7d4bdd55d92b7167
|
|
BLAKE2b-256 checksum How to use checksums |
a29b464d277468956c0dfe555eb7531dc0ced99d63966c3d3134cde0fd0e7ef3
|
| 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 Oct 3, 2026.
Transparency logRelease files / django_mail_preview-0.1.0-py3-none-any.whl
| Download URL | django_mail_preview-0.1.0-py3-none-any.whl |
|---|---|
| Size | 40.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ef1af21393d58666583a5693a26464316255704ad6c172be37342ba5abc09a6a
|
|
BLAKE2b-256 checksum How to use checksums |
6c2231ff351f85e4cb591643aa93ca616a597a5bb9ecaa5549af10a720781c36
|
| 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 Oct 3, 2026.
Transparency log