HTML email: you may love it, you may hate it — but you have heard of it. I bet you have an opinion on it, too. But did you know that HTML email is historically one of the most valuable and important communication channels for companies?
When it comes to marketing and broadcast emails (e.g. all that stuff that gets dumped into the Promotions tab in Gmail), companies can expect an average of $35–44 back on every dollar they spend, depending on who you ask. Email’s ROI routinely beats out that of affiliate marketing, paid search, display ads, and social media, making it a vital tool for every company.
Transactional emails — those triggered based on some action (like a purchase, account update, shipping notification, etc.) — are even more valuable. One study, from Experian, puts the value of transactional email at 8x that of marketing and broadcast emails.
While a lot of developers won’t work on marketing or broadcast emails, most will touch transactional email campaigns at some point. In fact, developers are often the only ones with direct control over transactional emails, since so many of them are app-generated or tied directly to the codebase.
Since transactional emails are proven to generate massive ROI for companies, and the best developers are ones that understand the business, it’s vital that developers understand the basics of HTML email.
Here’s what you need to know.
There are two main types of email: Marketing (or broadcast) and transactional.
Marketing emails are what you probably think of when you think about HTML emails. They are sent to a list of people that have signed up to receive them. They can be coupons, newsletters, event invitations, or lots of other things, but they are one-to-many. Even though they can (and should) be personalized, it’s still a large group of people receiving an email from a single sending event.
Transactional emails, on the other hand, are one-to-one communications and are sent in reaction to some triggered event. These are things like receipt emails, shipping notifications, and servicing emails like password resets and account update notifications.
There is sometimes a bit of gray area between the two, but it’s important to try to bucket any email into one of those two categories, especially when you start considering subscriber permissions. With GDPR taking effect on May 25th, anyone sending email is required to gather explicit permission from subscribers and maintain a record of that permission, or face hefty fines.
When it comes time to start sending HTML emails — whether they are marketing or transactional — the first step is setting up your sending environment. Even though most people will opt for a third-party email service provider (ESP) like Campaign Monitor, MailChimp, or Salesforce Marketing Cloud (or Postmark and Sendgrid for transactional stuff), it’s still necessary to understand the underlying infrastructure and authentication process when sending emails. ESPs will handle a lot for you, but you’ll need to set up the proper authentication records to ensure that your emails are delivered to subscribers.
Email works through the Simple Message Transfer Protocol (SMTP). It relies on a series of checks made by an ISP to ensure that you are who you say you are when sending an email. These are things like IP address and reverse DNS lookups. While these are a good starting point, there are three other authentication methods that must be properly set up to ensure good deliverability.
Sender Policy Framework (SPF) is a record stored on your domain that provides an additional check for authentication. Basically, you add SPF and TXT records in your DNS settings on the domain from which you send emails, which include the IP address of your mail server. ISPs can then verify your IP address as being legit, making it more likely that your email will be delivered. An SPF record looks something like this:
The Sender Policy Framework Project has more information about SPF records and syntax over on their website.
DomainKeys Identified Mail (DKIM) is another mechanism to authenticate your message. With DKIM, you are essentially setting up keys with which to take responsibility for the email. DKIM signs an email with a private key, which is then decoded with the help of a public key. The public key is another DNS entry for your sending domain that looks similar to this (taken from the DKIM website):
Finally, there’s Domain-based Message Authentication, Reporting and Conformance (DMARC). While DMARC isn’t technically an authentication mechanism itself, it informs a recipient that a message is protected by other mechanisms like SPF and DKIM. If either of those mechanisms fail, DMARC tells the recipient what to do with the message: Pass, quarantine, or fail it. It then provides a mechanism for notifying the sender about what happened to the message. It’s a powerful way to monitor your sending reputation and see who (if anyone) is abusing your mail infrastructure.
Just like the two mechanisms above, DMARC lives as a DNS entry, but this time on a subdomain like _dmarc.example.com. You can set thresholds in DMARC using the pct tag then tell the recipient what to do with those emails using the p, or policy, tag, which takes the values none, quarantine, and reject.
It’s important to note that DMARC works in concert with other authentication protocols. When properly configured, all of them are part of the delivery chain that helps prevent spam.
Although there are other authentication methods like SenderID and DomainKeys, they are implemented by fewer ISPs. I’ll leave it to you to research those further.
Once you have your domains properly authenticated and ready to send email, it’s time to start creating those emails. At first, you may think that HTML emails are simple HTML documents. But there’s more to it than that.
Email messages are a mix of both the message itself as well as the headers that identify information about that message. When it comes to deliverability, key/value pairs in the header like the from name, sender, subject, and return-path tell email clients who is sending the message. Many of these are used with the mechanisms mentioned above to authenticate the message.
But there is another key/value pair that builds the actual message. This is the Content-Type key, which takes a value like multipart/alternative to notify the recipient that there are multiple parts in the message and to display the appropriate part to the subscriber based on their preferences. When multiple parts are included in a message, the email is referred to as a Multipurpose Internet Mail Extensions (MIME) email.
There are two main content types used in all HTML emails, the text/plain and text/html types. The HTML type is where the actual HTML is injected into the message, while the plain type is where a plain text version of the email lives. For deliverability purposes, you should always include a plain text version of your email that closely matches or relates to the HTML version of your email.
There are additional content types that can be used, but they have very limited support from ESPs. The text/watch-html type can be used to send a limited HTML version of your message to Apple Watch users, resulting in rich text-like messages.
More recently, the text-x-amphtml has been introduced by Google’s AMP Project and would allow for richer, more interactive emails when using the AMP markup.
It should be noted that, as of writing, the project is still in its early phases and no ESPs support adding the AMP MIME type to an outgoing email, making it virtually useless outside of Google’s developer preview and test environment.
Building a full email message and accounting for the various headers and MIME parts can be complex. Fortunately, most ESPs take care of the hard work for you, allowing you to upload just your HTML and plain text versions of a message. Still, it’s important to know how these messages are constructed, since a lot of companies still build in-house email services without the safety net of a third-party ESP.
Although you’re probably on the cutting-edge of web development, HTML email tends to trail behind the web in terms of what markup is used for coding campaigns. You may be used to using divs and semantic markup, coupled with the latest in CSS like flexbox and grid, but most email clients in popular use only support a subset of HTML and CSS. Due to security concerns and aggressive (and often outdated) rendering engines, email clients will strip, alter, or ignore a lot of HTML and CSS. Campaign Monitor has an excellent guide to what’s supported where in all major email clients.
That’s why most email campaigns today rely on tables to structure content and inline CSS to style that content. If you’re creating simple, single-column emails, you can get away with div-based emails, but for anything requiring multiple columns, you’re stuck with using tables in some capacity.
Let’s go over the basics.
The structure and content of your emails will largely live inside HTML tables. Although painful, you don’t have to worry about all of the table-related tags, just three: The table (table), table row (tr), and table cell (td). The table will be the container of your email, with the row and cell being individual modules within that email.
For example, if you have a simple email with a logo, headline, image, paragraph, and button, that structure will look something like this:
Each of those individual elements can be its own module, in its own table row and table cell. Keeping emails modular allows you to more easily troubleshoot issues within the email, as well as quickly combine different types of modules to build various types of email layouts.
For accessibility reasons, you should include the ARIA attribute role=”presentation” on every table element within your email. This will prevent screen readers from reading the content within as tabular data, making the email easier to understand.
Within those modules, you should rely on familiar semantic markup for your content. If you’re marking up a headline, use heading elements like h1, h2, etc. If you have a paragraph of copy, use a p tag. Not every email client supports newer semantic tags like header or article, but for people using screen readers, that added semantic value can be very helpful.
Images are a bit more complex, but not too bad. Some older email clients like to add white space around images or don’t properly interpolate or constrain image sizes based on the size of its parent element. Both instances can lead to broken layouts and poor user experiences. Additionally, since nearly half of all users are opening emails on mobile devices, we want images to be responsive by default across devices. It’s a little more verbose, but this markup will cover all those bases in emails:
<img alt=”descriptive alternative text” src=”https://example.com/img/example.jpg" width=”600" border=”0" style=”display: block; max-width: 100%; min-width: 100px; width: 100%;”>
For non-decorative images, alternative text should always be included. This is especially important in email, as many email clients block images by default. In these cases, the alt text will be displayed. You can even style that in many clients by applying things like color and font-size to the image tag.
The width attribute is used for clients that don’t infer the correct image size and the border attribute it set to zero to prevent blue borders around linked images. Using display: block; ensures that extra white space isn’t added around the image. And the combination of max-width, min-width, and width allow the image to scale up and down based on the device size without blowing out email layouts or getting too small on tiny devices.
The use of responsive-by-default images and semantic markup within table-based layouts can get you 90% of all email work. For that other 10%, you’ll probably need to dig into more complex techniques like Hybrid Coding, which uses fluid layouts and fixed-width, Microsoft-conditional code to achieve robust, responsive email campaigns. Check out my online bookmarking site for everything email for more resources.
When it comes to styling content, you can keep things relatively simple. For maximum compatibility, you should apply styles inline on the specific HTML elements you’re trying to style. While something like 90% of major email clients now support embedded styles, you’ll still get a handful of clients that won’t display embedded styles, leading to poorly formatted emails. I recently wrote about the debate between embedded vs. inline styles if you want to read up on the subject.
For text, the basic CSS properties of color, font-family, font-size, font-style, font-weight, and line-height will work wonders, as they are universally supported in all major email clients. For block-level elements like headings and paragraphs, you may want to override the margin property to remove additional white space. It’s often more reliable to include white space as padding on the individual table cells themselves, since some older clients don’t like the margin property.
You can even use web fonts in a lot of email clients the same way you would online. Use the @font-face CSS rule or link out to an external stylesheet and call them in the font stack. Just include solid fallback fonts for clients that don’t support web fonts.
Buttons are slightly more complex in emails. Although I typically recommend you turn links into block-level elements and use a combination of background-color, color, border, border-radius, and padding to build buttons just like you do on the web, some email clients don’t properly render all of these properties.
There are a few different techniques for building what are called “bulletproof buttons” in the email world, using borders, padding, and a combination of both. If you need something truly bulletproof, then you can always use Campaign Monitor’s excellent Bulletproof Button generator, which will give you a combination of HTML and Microsoft’s proprietary Vector Markup Language (VML) that will work everywhere. Just don’t use images for buttons if you can help it. As I mentioned before, many email clients block images by default, so any image-based buttons won’t be seen and won’t be clicked on.
If you’re not keen on hand-writing HTML and CSS for email, there are a few tools out there to help make creation easier. Chances are good that you’re comfortable working in the milieu of frameworks that abstract away some of the complexity of a domain and rely on convention to make development easier. If this is the case, frameworks like MJML and Foundation for Emails could be the perfect fit.
Both provide simple markup, built-in components, and build tools that allow you to quickly develop email code that works well across clients. A quick GitHub search will pull up a ton of templates, frameworks, and tools for creating HTML emails, too.
Although the markup and techniques described above are fairly simple, don’t let that fool you. Emails don’t have to be static. HTML email allows for some seriously complex and powerful applications, like adding interactivity to email, allowing users to experience rich interactions right in their inbox.
Email agency REBEL has pioneered a lot of this work. They have built everything from simple carousels to full retail checkout experiences into email campaigns. They’ve even opened up an API to let developers build that interactivity into their own campaigns without having to create everything from scratch.
If you do want to roll your own interactivity from scratch, read up on what email developer ace Mark Robbins calls the “punched card coding” technique. Using simple HTML radio and checkbox buttons, combined with CSS that looks at the state of those buttons and applies conditional formatting, you can create some truly amazing experiences for your subscribers.
HTML Email might not be your first choice for a project, but it’s an incredibly powerful tool for every company. Both marketing and transactional emails provide obscene ROI, allowing you to put your development skills to work to directly impact your business.
What’s more is that — as popular as email is among consumers — many companies leave email as an afterthought. Email standards are absurdly low, so if you’re developing emails that are even slightly better than the competition, you have an opportunity to achieve great things, impacting not only your business but the lives of your subscribers, too.
Install LogRocket via npm or script tag.
LogRocket.init() must be called client-side, not
Implementing OTA in-app updates in React Native apps can streamline the update process, preventing delays that hinder overall productivity.
StyleX is a build-time, type-safe CSS-in-JS library recently open sourced by Meta. Explore StyleX and the evolution of styling libraries.
Learn to set up a completely custom Astro ecommerce implementation that’s also highly performant and type-safe in this straightforward guide.
Let’s build a Next.js app that implements vector search using Supabase and OpenAI to offer better search experiences for users.