Sending HTML emails

Clément PREVOT
Snowball Insider
8 min readJun 17, 2024

--

Sending HTML emails is not as easy as one can think. Email clients are stuck in the 90’s when it comes to handling HTML and CSS technologies.

A quick example

At Snowball, we are currently building the list of newsletter editions in the media web app:

  • We want to display each edition as a card
  • We want to display 3 cards per line and that it automatically wraps to the next line
  • I want all cards on a line to be the same height

Something like that:

Pretty easy, isn’t it?
You wipe out your basic Tailwind CSS config, you write the following code:

<div class="flex gap-2 p-2 justify-center flex-wrap max-w-[1200px] mx-auto items-stretch">
<div class="flex flex-col gap-1 max-w-[300px] border-1 p-2">
<img
src="https://picsum.photos/id/237/200/300"
class="max-w-full h-auto max-h-[200px] fit-cover"
alt=""
/>

<h1>This is the title</h1>

<p class="flex-1 flex items-end">This is the subtitle</p>

<div class="flex justify-between">
<span>Yoann Lopez</span>
<span>June, 15th 2024</span>
</div>
</div>

<!-- Rince and repeat for all the editions... -->
</div>

And you send that with the Tailwind-generated stylesheet to your Gmail inbox:

😱

Well… it doesn’t seem to work properly (this is an understatement).

If you want to approach the expected result, while being compatible with most of mail clients, you will have to write the following markup:

<table style="max-width: 1200px; border-collapse: collapse; margin: 0 auto">
<tbody>
<tr>
<td style="padding: 16px; vertical-align: top">
<table style="max-width: 300px">
<tr>
<td colspan="2" style="width: 100%">
<img
src="https://picsum.photos/id/237/200/300"
style="
width: 100%;
height: auto;
max-height: 200px;
object-fit: cover;
"
alt=""
/>
</td>
</tr>
<tr>
<td colspan="2">
<h1>This is the title</h1>
</td>
</tr>
<tr>
<td colspan="2">
<p>This is the subtitle</p>
</td>
</tr>
<tr>
<td>Yoann Lopez</td>
<td style="text-align: right">June, 15th 2024</td>
</tr>
</table>
</td>

<!-- Another <td /> for the second card -->

<!-- Another <td /> for the second card -->
</tr>
<!-- Manual wrapping! -->
<!-- You'll need to generate your HTML using a bit of JS. -->
<tr>
<td colspan="3" style="padding: 16px; vertical-align: top">
<table style="max-width: 300px; margin: 0 auto">
<tr>
<td colspan="2" style="width: 100%">
<img
src="https://picsum.photos/id/237/200/300"
style="
width: 100%;
height: auto;
max-height: 200px;
object-fit: cover;
"
alt=""
/>
</td>
</tr>
<tr>
<td colspan="2">
<h1>This is another title a little bit longer</h1>
</td>
</tr>
<tr>
<td colspan="2">
<p>This is another subtitle</p>
</td>
</tr>
<tr>
<td>Yoann Lopez</td>
<td style="text-align: right">June, 15th 2024</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>

Here is the result in you inbox:

This is a bit better

As you can see, it’s not perfect, but it’s kind of close…
On top of not being perfect:

  • You’ll have to get rid of Tailwind CSS (because it generates too big of a stylesheet and the mail clients will reject it)
  • You’ll have to use inline style (because external stylesheets are not supported at all and embedded stylesheets are not supported on every client)
  • You’ll have to use a semantically incorrect table to display properly your content
  • The code is awful!

How to write a good email

Thankfully, some sites are here to help us navigate our way through that hell:

Here are the main takeaway and learnings if you ever need to write an HTML email

  • Do not use modern CSS tech like Flexbox to layout your email. You must instead use good old tables.
  • Do not write your styles as stylesheets (embedded or even worse, linked). Though stylesheets are supported in some clients (like Gmail), there are some quirks about them (e.g. only in head, maximum 16ko, if any property in a stylesheet is not supported, then the whole stylesheet is dropped, …)
    You must instead inline all your styles. Some tools can help you do that:
    * Premailer
    * CSS Inliner Tool de Mailchimp
    * customer.io integrates a CSS pre-processor in their “broadcasts” to handle this.
  • Not all CSS selectors and properties are supported, and support greatly depends on the client. There could even be some differences in the same client among its different apps (e.g. differences between GMail desktop webmail, iOS app, Android app, and mobile webmail) and of course versions (e.g. big differences between the different versions of the Outlook desktop client). Be careful because some clients (Hello GMail) will even drop all your styles if only one property in it is not supported. There are also some weird cases (hello again Gmail) where styles are supported in one “viewing place” but not in another (e.g. you can use embedded stylesheets in the main GMail inbox, but it doesn’t work when opening the full email as a web view if it has been truncated). Even weirder, some clients (hello once again Gmail) support some CSS properties when used with a Google Account only or even when used with a non-Google Account (the infamous GANGA) You will have to make some choices about what you want to support and how. Do you want a graceful version for some clients (e.g. add the box-shadow properties not supported by Gmail but supported by Apple Mail)? Do you use properties known to break some clients (and thus “not supporting” those clients)? Etc.
  • Emails can get “clipped” (or truncated). For example, Gmail will clip your emails at 102Kb precisely, leaving no other choice to your user to either use the “full mail view” of their client (which, as seen above, can have different CSS/HTML support) or use a web view (recommended). Of course, the clipping behavior and/or length completely depends on the client and version…

If you want a shortlist of the best/worst globally non/badly supported features, here it is:

  • You can’t use flexbox, use <tables /> instead.
  • You can’t use SVGs, use plain old images instead.
  • You can’t use custom fonts, rely on system fonts instead (however note that you can write a dedicated stylesheet in the <head /> of your mail to add your custom fonts for clients supporting custom fonts)
  • You can’t have box-shadow (but some clients are supporting it, so you can add the property anyway for those). If you really need shadows, you will have to resort to the old-school method of images in tables.
  • You can’t position elements (particularly not in fixed, sticky or absolute positions).

What about reputation?

Reputation: a good email reputation ensures higher deliverability rates, meaning emails are more likely to reach the recipients’ inboxes rather than being filtered out as spam. Conversely, a poor reputation can result in emails being blocked or sent to the spam folder.

Your reputation depends on a lot of factors: your domain name, the server your email is coming from (i.e. the reputation of the sender, e.g. Sendgrid, …), the content of your message, the presence or absence of some headers, the status of your links and images, the accessibility, etc.

Do not underestimate the importance of proper DNS configuration for those cases (SPF, DKIM, DMARC, …) as it could greatly improve/balance the SpamAssassin note.

The providers often update their recommendations (e.g. Yahoo and Gmail here) so it’s highly recommended to stay on top of those recommendations if you want your user to be able to read your email.

One big improvement to your “reputation” is to provide a List-Unsubscribe header with your email. This header will allow your users to easily unsubscribe from your newsletter thanks to some easily accessible buttons/links directly in their client

One big thing to note about SpamAssassin (and mail providers globally) is how they dislike “exotic” domain TLDs, like .xyz (🙄 snowball.xyz I’m looking at you). You will have a really bad reputation using those kinds of domains to send emails or even using links with these domains in your email.

One way to work around this is to use a dedicated “legit” domain (e.g. media-snowball.com instead of snowball.xyz) to send emails, host your email assets, and redirect to your actual product hosted on an “exotic” domain.

Also, using a “tracker” URL from your email-sending provider can be a workaround solution to the poor reputation of your links. For example, Sendgrid automatically replaces any link inside of a sent email with a specific URL for tracking. Suppose you configure your “legit” domain as a tracker domain. In that case, all your “exotic” URLs will be replaced by a specific “legit” URL with, bonus, tracking (e.g. any snowball.xyz URL in an email will be automatically replaced by Sendgrid with a url4000.media-snowball.com/ls/click?upn=<…> URL, thus hiding the snowball.xyz URL).

You can also easily write a simple HTML/JS page that can handle redirection and that you can host on your legitimate domain.

Write the best email template for your use case

Regarding clients support, the best is to assess what clients/OS/versions are used among your reader base.

💡 Here is an example of the kind of poll you could run to assess that

Which email client/provider are you using?- Apple
- Gmail (Google account)
- Gmail (non-Google account)
- Yahoo
- Orange
- SFR
- Free
- La Poste
- ProtonMail
- Others
How do you check your emails?- Webmail on a desktop/laptop computer
- Webmail on mobile
- Windows client (like Outlook, Mozilla Thunderbird, ...)
- MacOS client (like Apple Mail, Outlook, Mozilla Thunderbird, ...)
- Linux client (like Mozilla Thunderbird, ...)
- iOS App
- Android App
- Others

Some tools to help you

Also here are some neat tools to test and assess the format and validity of your emails:

  • Email On Acid will help you build, optimize, preview, test, and validate your email. The solution is quite expensive though
  • Mail Tester provides you an email address to send your email to and will then assess the “Spam level” of your email based on multiple criteria: domain, headers, content, SpamAssassin result, broken links or images, URL shortener, … It will then give you a note from 1 to 10 and some advice to improve your reputation level.

💡 With the free version of Mail Tester, you can only test 3 mails per day. The check is made on IP address… 😉

  • Sendgrid offers a way to test email on multiple clients. You’ll have to use 1 credit (you will have to pay for each credit and you can only buy credit if you are a paying Sendgrid customer) per client you want to test, but then it will run your email in those clients and present you screenshots and reports. Super useful.

That’s it.
I hope you found it helpful.

If you have any questions, don’t hesitate to leave a comment!

Clement for Snowball❤️
Thanks to Yoann Lopez for helping redacting this article 🙏

--

--

Clément PREVOT
Snowball Insider

Lead Tech @ Snowball Former Senior Frontend dev and DevXP engineer @ BackMarket Former Frontend lead @ LumApps