Intro to writing HTML emails for web developers

Writing and styling HTML emails has always been a giant pain in the ass. There are no "standards" for what markup and styles are supported among the multitude of email clients out there, so it's a minefield of figuring out what will work and what won't.

Below is a crash-course on how to avoid common pitfalls and create emails that both look good and display consistently for your recipients. I'll be assuming that you're already familiar with the basics of web development.

Contents

A brief rant history

The current, horrific state of email markup can't be blamed on any one person or company — the fact that no email client supports the full HTML / CSS spec is a pretty clear indicator of that. That being said, Microsoft's Outlook 2007 is really where things started to go south. This set the closest thing a "standard" that email will likely get for the foreseeable future for email clients, given Microsoft's ubiquity within the business world.

That's because, starting with Outlook 2007, the HTML / CSS rendering engine is the same that powers Microsoft Word(!), which has pretty terrible support for CSS. What's worse, even at the time this was clearly downgrade from Outlook 2003, which used Internet Explorer's rendering engine. Which, you know, was actually built for web content.

In true Microsoft fashion, instead of learning from their mistake, Microsoft doubled-down and continued using Word as the rendering engine until (checks notes)... oh, it's still being used even in Outlook 2021. Things are starting to look better: Microsoft's "new Outlook" (which started rolling out in late 2023) finally gets rid of Word as the rendering engine, and instead seems to be mainly taking inspiration from Outlook on the Web.

This is a huge step back to normalcy, and this hopefully will lead to the majority of this article being obsolete in a few years — Microsoft has end-of-support for Outlook 2021 marked for October 2026. I'm not holding my breath, though, since the precedent that Microsoft set with Outlook 2007 can still be seen in other email clients.

Some basics

What counts as an "email client"?

The obvious answer would be "a desktop application to view emails", but that's only partially correct. Email providers often provide their own web-based method of viewing emails (e.g. Gmail, Outlook on the Web, GoDaddy, etc.), and even those have varying degrees of support for the full HTML / CSS specification.

These aren't just minor divergences from the HTML and CSS specs, either. For example, style tags are stripped out of the email body (Gmail / G Suite) or are removed entirely (AOL Mail, which still had 1.5 million paying users in 2021).

How can I find out what's supported?

When creating emails, I basically have two resources open at all times:

The two of these together will give you a pretty good idea of what's supported and what isn't by each email client. The former is a bit more detailed, but the latter is more up-to-date.

Additionally, GitHub user seanpowell created a solid boilerplate that implements several best practices, as well as resets some email client-specific styles that can get in the way of your design process.

General tips

Please for the love of god test your emails everywhere you can

This is the most important tip I can give. Don't assume that what looks good in a browser or a single email client will look remotely similar in another.

You can use services like Email on Acid, Litmus, https://testi.at/, etc. to do these tests for you, but if you're working small-scale (e.g. a single custom email, or an email signature) or are on a budget, there's absolutely nothing wrong with sending test emails to yourself and opening them in as many clients as you can.

At minimum, you should be testing in:

  • Outlook (any version from 2007 to 2021)
  • "new" Outlook
  • Outlook's mobile app
  • Gmail

And if you can, also test in:

  • Apple Mail
  • Outlook for Mac

"Code like it's 1999"

This is a common joke among email developers, but it still holds true. The more you can stick to the HTML 4.01 spec, the better. Effectively, this means no modern HTML tags or CSS properties, and styles should be converted to attributes wherever applicable.

For example, instead of this:

<img src="https://cataas.com/cat?type=square" alt="A picture of a cat" />
<hr />

you'd be better off with this:

<img
  src="https://cataas.com/cat?type=square"
  alt="A picture of a cat"
  height="48"
  width="48"
/>
<!-- prettier-ignore-start -->
<hr>
<!-- prettier-ignore-end -->

Notice that the width and height CSS properties were converted to pixels and moved to HTML attributes — Outlook 2007–2023 has only support for the CSS property some tags. Additionally, we replaced the self-closing hr tag with an "open" one; this isn't good HTML 5, but it is valid HTML 4. The prettier-ignore comments are required if you use Prettier for code-formatting, so it doesn't automatically self-close the <hr> tag.

Use tables when alignment matters, not divs

This is probably the most important, non-obvious rule. Tables are essentially the only way to ensure consistent alignment and spacing in any context. So, instead of this:

<div>
  <div style="display: inline-block; width: 100px; height: 100px;">
    First item
  </div>
  <div style="display: inline-block; width: 100px; height: 100px;">
    Second item
  </div>
</div>

do something like this instead:

<table>
  <tr>
    <td style="width: 100px; height: 100px;">First item</td>
    <td style="width: 100px; height: 100px;">Second item</td>
  </tr>
</table>

Notice that we lose some of the responsiveness that we're used to with divs — the two elements will always be side-by-side, regardless of the width of the viewport. This will be a common theme — consistency at the cost of flexibility.

Use inline styles, not CSS classes

This is likely the biggest pain point for web developers. The final, "production" build of an email should use inline styles due to inconsistencies with how email clients handle style tags. This means, however, that things like media queries and pseudo-selectors become impossible to use (although support for them was already spotty at best to begin with).

The easiest way to handle this is using classes like normal during development, and use a post-processing tool to convert everything to inline styles, which will convert this:

<head>
  <style>
    .red {
      color: red;
    }
  </style>
</head>
<body>
  <p class="red">This text will be red</p>
</body>

to this automatically:

<body>
  <p style="color: red;">This text will be red</p>
</body>

This, of course, gets tricky when combined with the "Code like it's 1999" section above and dealing with HTML attributes. This leads me into my penultimate tip:

Use a build tool purpose-built for email

There are a few tools out there that are specifically designed to make writing easier. I love using Tailwind CSS for web development, and it turns out that a tool called Maizzle has been created to allow you to use it in emails as well!

In addition to having the ability to convert classes to inline styles, Maizzle also has a component system that allows you to create reusable components and abstract repetitive code. This allows for this:

<body>
  <table>
    <tr>
      <td style="padding: 10px;">
        <h1>I've come to make an announcement!</h1>
        <p>[redacted]</p>
        <img
          src="https://cataas.com/cat?type=square"
          alt="A picture of a cat"
          height="48"
          width="48"
        />
        <img
          src="https://cataas.com/cat?type=square"
          alt="A picture of a cat"
          height="26"
          width="26"
        />
        <img
          src="https://cataas.com/cat?type=square"
          alt="A picture of a cat"
          height="86"
          width="86"
        />
      </td>
    </tr>
  </table>
</body>

into something a bit more manageable:

<body>
  <x-content>
    <h1>I've come to make an announcement!</h1>
    <p>[redacted]</p>
    <x-cat-image size="48" />
    <x-cat-image size="26" />
    <x-cat-image size="86" />
  </x-content>
</body>

Don't be afraid to use Outlook-specific workarounds

Finally, given how restrictive Outlook is, there is absolutely nothing wrong with targeting all other email clients first, then using Outlook-specific styles to fill any gaps. This is done in two ways:

  1. Outlook-specific CSS properties — some important ones:
    • mso-line-height-rule:
      • exactly: prevents Outlook from overriding line-height to 20px on td and img tags.
      • at-least (default): allows the line-height to grow vertically to fit content, e.g. images.
    • mso-line-height-alt: since Outlook's support for the line-height property isn't great, use this property in addition to the normal property, e.g. line-height: 12px; mso-line-height-alt: 12px;.
    • mso-table-lspace / mso-table-rspace: set to 0pt on table tags in your reset stylesheet to remove Outlook's unique table spacing.
    • -ms-interpolation-mode: when set to bicubic on img tags, makes images display much more cleanly if it's not displayed at the image's native size. Helpful in Internet Explorer and Outlook 2003 and earlier.
  2. Outlook conditional comments — the most important statement is <!--[if mso]> Outlook-specific code <![endif]-->, which allows us to define Outlook-specific HTML that's ignored in all other clients. This is incredibly powerful, but can lead to duplicate or hard-to-manage code if overused — use it when nothing else is working.

Final thoughts

Writing HTML emails is a pain, but it's a necessary evil (for now). Hopefully this has been a helpful crash-course on how to avoid common pitfalls and point you in the right direction! Once you get a grasp of the general things to watch out for, you'll be able to pump out emails in no time.

I know I already called these resources out above, but here they are again for easier access: