Files
hyteck-blog/content/post/about-html-mails/index.md
moanos 2388e78c5f
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
feat: Add thoughts on html mails post
2025-07-12 12:48:20 +02:00

5.9 KiB

title, date, draft, image, categories, tags
title date draft image categories tags
Thoughts on HTML mails 2025-07-12T12:05:10+02:00 false uploads/html-e-mails.png
English
email
html
plaintext
django
notfellchen

Lately I worked on notification e-mails for notfellchen.org. Initially I just sent text notifications without links to the site. Terrible idea! An E-Mail notification I send always has Call-to-Action or at minimum a link to more information.

I left the system like this for half a year because it kinda worked for me (didn't suck enough for me to care), and I was the main receiver of these notifications. However, as the platform is developed further and more users join I need to think about more user-centric notifications.

So what do I imagine is important to a user? *

  • Information benefit: An e-mail has the purpose to inform a user. This information should be immediately visible & understandable.
  • Actionables: Users should be able to act on the information received. This is the bright red button "DO SOMETHING NOW!" you see so often.
  • Unsubscribing: Informing e-mails stop is not only a legal requirement and morally the right thing to do but it also gives users agency and - I hope - increases the User Experience

With these I naturally came to the next question: Plaintext or HTML?

Some people would say Plaintext is inherently better than HTML e-mails. Many of these reasons resonate with me including:

  • Privacy invasion and tracking
  • HTML emails are less accessible
  • Some clients can't display HTML emails at all
  • Mail client vulnerabilities

These are all valid points and are a reason I generally enjoy plaintext e-mails when I receive them. But this is not about me but users. And there are some real benefits of HTML e-mails:

  • Visually appealing: This is subjective but generally most users seem to agree on that
  • User guidance: Rich text provides a real benefit when searching for the relevant information

Be honest: Do you read automated e-mails you receive completely? Or do you just skim for important information?

And here HTML-mails shine: Information can easily be highlighted and big button can lead the user to do the right action. Some might argue that you can also a highlight a link in plaintext but that nearly always will worsen accessibility for screen-reader user.

The result

In the end, I decided that providing plaintext-only e-mails was not enough. I set up html mails, mostly using djangos send_mail function where I can pass the html message and attattching it correctly is done for me.

A screenshot of an e-mail in thunderbird. The e-mail is structured in header, body and footer. The header says "Notfellchen.org", the body shows a message that a new user was registered and a bright green button to show the user. The footer offers a link to unsubscribe

For anyone that is interested, here is how most my notifications are sent

def send_notification_email(notification_pk):
    notification = Notification.objects.get(pk=notification_pk)

    subject = f"{notification.title}"
    context = {"notification": notification, }
    if notification.notification_type == NotificationTypeChoices.NEW_REPORT_COMMENT or notification.notification_type == NotificationTypeChoices.NEW_REPORT_AN:
        html_message = render_to_string('fellchensammlung/mail/notifications/report.html', context)
        plain_message = render_to_string('fellchensammlung/mail/notifications/report.txt', context)
    [...]
    elif notification.notification_type == NotificationTypeChoices.NEW_COMMENT:
        html_message = render_to_string('fellchensammlung/mail/notifications/new-comment.html', context)
        plain_message = render_to_string('fellchensammlung/mail/notifications/new-comment.txt', context)
    else:
        raise NotImplementedError("Unknown notification type")

    if "plain_message" not in locals():
        plain_message = strip_tags(html_message)
    mail.send_mail(subject, plain_message, settings.DEFAULT_FROM_EMAIL,
                   [notification.user_to_notify.email],
                   html_message=html_message)

Yes this could be made more efficient - for now it works. I made the notification framework too complicated initially, so I'm still tyring out what works and what doesn't.

Here is the html template

{% extends "fellchensammlung/mail/base.html" %}
{% load i18n %}
{% block title %}
    {% translate 'Neuer User' %}
{% endblock %}

{% block content %}
    <p>Moin,</p>
    <p>
        es wurde ein neuer Useraccount erstellt.

    </p>
    <p>
        Details findest du hier
    </p>
    <p>
        <a href="{{ notification.user_related.get_full_url }}" class="cta-button">{% translate 'User anzeigen' %}</a>
    </p>
{% endblock %}

and here the plaintext

{% extends "fellchensammlung/mail/base.txt" %}
{% load i18n %}
{% block content %}{% blocktranslate %}Moin,

es wurde ein neuer Useraccount erstellt.

User anzeigen: {{ new_user_url }}
{% endblocktranslate %}{% endblock %}

Works pretty well for now. People that prefer plaintext will get these and most users will have skimmable html e-mail where the styling will help them recognize where it's from and what to do. Accessibility-wise this seems like the best option.

And while adding a new notification will force me to create

  • a new notification type,
  • two new e-mail templates and
  • a proper rendering on the website

this seems okay. Notifications are useful, but I don't want to shove them everywhere. I'm not running facebook or linkedin after all.

So for now I'm pretty happy with the new shiny e-mails and will roll out the changes soon (if I don't find any more wired bugs).

PS: I wrote this post after reading blog & website in the age of containerized socials by ava. Maybe this "Thoughts on" format will stay and I will post these in addition to more structured deep dives.