Effective email HTML and image handling
Tags: webdev, email, html, aws
Published: 2023-12-04 12:15:11

  If you are trying to fire an email you coded with all of the beautiful assets your UX team provided, only to find an unaligned mess, without images when opening it on your email provider, this article is for you.

  The reason behind this is quite simple: Modern CSS is not supported in most email providers. As a developer of the 2020s, you're likely accustomed to aligning your pages with flexbox, grid or other modern CSS features.

  Now, on reading the above statement you might be thinking this is probably an issue with small email providers that nobody uses anyway so why should you care?

  To our dismay, major platforms like Gmail do not support flexbox align-items, and there are no plans to include support for SVG (and don't get me started with outlook's desktop app, where even basic styles go very wrong). You can refer to an illustrative guide of what you can or can't do across email providers on [Caniemail](https://www.caniemail.com/).

  I first learned about these issues in a presentation by a work colleage (@mfornaciari), and was later on assigned a task to develop e-mails for an e-commerce app. This article is a way for me to summarize the main points I learned while studying for my tasks.

### Basic HTML Alignment and Layout

  So, if you can't use modern CSS, what is the go to way of aligning emails? the answer is, a simple html table:

  ```html
  <table
    role="presentation"
    width="100%"
    border="0"
    cellpadding="0"
    cellspacing="0"
    style="min-width: 100%;"
  >
    <!-- since we are using the table tag only for styling, it is important for acessibility purposes to use a semantic role -->
    <!-- the other properties, except for the width, are style resets -->
    <tr>
      <td align="center">
        {content}
      </td>
    </tr>
  </table>
  ```

  This simple centered table layout is widely supported and ensures a consistent appearance across various email clients, even those that might not interpret advanced styling techniques.

  With this structure, I separated my email content into rows. Since media queries are not reliable for emails, it's best to think of them as a verticalized media that must fit web and mobile screen resolutions. For this reason, it is a common practice to limit an email's body width to 600px.

  Since I was using Ruby on Rails, I added this rule to my mailer layout file along with basic text styles and style resets:

  ```html
    <!DOCTYPE html>
      <html>
        <head>
          <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
          <style>

            body, p, span, ul, li {
              font-size: 16px;
              font-family: Urbanist, sans-serif;
              color: #000;
              margin: 0;
              padding: 0;
              text-align: left;
              line-height: 1.75
            }

            h1, h2, h3 {
              font-family: Urbanist, sans-serif;
              margin: 16px 0;
              padding: 0;
              line-height: normal;
              color: #000;
            }

          </style>
        </head>

        <body>
          <table role='presentation' width='100%' border='0' cellpadding='0' cellspacing='0' style='background: grey; min-width: 100%;'>
            <tr>
              <td align='center'>
                <table role='presentation' width='100%' border='0' cellpadding='0' cellspacing='0' style='background: #fff; max-width: 600px'>
                  <tr>
                    <td align='center'>
                      <%= yield %> <!-- For those unfamiliar with html.erb, this is where the content of my specific e-mails will go -->
                    </td>
                  </tr>
                </table>
              </td>
            </tr>
          </table>
        </body>
      </html>
  ```

  Now, lets create a simple welcome mail and check out the result in mailcatcher (keep in mind that in a real use scenario, you should test that your email actually worked in real providers):

  ```html
      <body>

        <!-- header, this can be extracted into a shared partial -->
          <header>
            <table
              role="presentation"
              width="100%"
              border="0"
              cellpadding="0"
              cellspacing="0"
              style="min-width: 100%;"
            >
              <tr>
                <td align="center" style='background: red'>
                  <h1 style='color: yellow'>Super e-commerce</h1>
                </td>
              </tr>
            </table>
          </header>

          <!-- content -->
          <table
            role="presentation"
            width="100%"
            border="0"
            cellpadding="0"
            cellspacing="0"
            style="min-width: 100%;"
          >
            <tr>
              <td align="center">
                <h1>Welcome to this plataform, champ</h1>
                <p>Thanks for joining, you're a real superstar, we love you</p>
              </td>
            </tr>
          </table>

          <!-- footer, this can be extracted into a shared partial -->
          <footer>
            <table
              role="presentation"
              width="100%"
              border="0"
              cellpadding="0"
              cellspacing="0"
              style="min-width: 100%;"
            >
              <tr>
                <td align="center" style='background: red'>
                  <h1 style='color: yellow'>cool logo</h1>
                </td>
              </tr>
            </table>
          </footer>

      </body>
  ```

  now let's fire our email, aaaand:
  
![Sample email](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aev0hrdkv9ta9fls2zc2.png)

  Your UX team might have a murderous instinc towards me upon seeing this, but that's what healthy emails are made of.
  
### Strategies for Handling Images

  Another issue I ran into was dealing with images. As I mentioned at the beginning of this article, Gmail does not support inline image methods such as SVG or base64.

  Also, emails have a limited size limit; Gmail, for instance, allows only a maximum of 25MB per email. Additionally, if you send multiple emails with the same subject, for instance, a user spamming password recovery emails and receiving many stacked emails with (1), (2)..., this limit counts for all the collective emails, not for each one.

  Here are the two best approaches I found for handling images

#### Image File attachment

  When it comes to including images in your emails, one simple method is to attach image files directly to the email. This allows the recipient to download and view the images separately. Keep in mind that the total size of email attachments may be subject to limitations imposed by email service providers.

  Example:

  ```ruby
    class YourMailer < ApplicationMailer
      def send_email_with_attachment
        # Other mail settings (to, from, subject, etc.)
        mail(to: 'recipient@example.com', subject: 'Email with Attachment') do |format|
          format.html do
            # Your HTML content here
          end

          format.text do
            # Your plain text content here
          end
        end

        # Attach the image file
        attachments['image.jpg'] = File.read('path/to/image.jpg')
      end
    end
  ```

  I feel like this method should be used with some consideration, since it increases the total size of your email.
  Additionally, it may not be ideal to clutter your email with image attachments, considering that the final user isn't intended to download an Instagram logo, for example.

#### External URL Hosting

  The approach I find most effective for all image sizes, is storing them in an external host such as on an S3 bucket, google drive, a github repository or imgur. This approach doesn't impact the email's full size limit, providing a more scalable solution, but the external services apis may have requests and bandwith limitations and may not be free.

  Example:

  ```html
    <img src="https://external-hosting.com/path/to/image.jpg" alt="External Image" />
  ```

  The challenge I found with this approach is that free services for hosting are not really optimized for this use and may lead to broken images in your email.

![Broken image example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xnxnvttsr4tb5n2p64vm.png)

  The mobile screenshot above depicts the footer of the email I was developing for my task. It was designed to feature a company logo and three social media links. Initially, I chose to use Google Drive to store my images because it's free. 
  
  While the web version of my app consistently loaded images upon most page reloads, the mobile version experienced frequent breaks. Unfortunately, relying on a simple JavaScript reload for images when a request fails is not an available feature in email providers, so the first request on your img tag must succeed.
  
  Consequently, I shifted to a paid solution – the S3 bucket. Although there is a cost associated with this approach, it proved to be a reliable solution for image loading. At first this solution did not seem ideal for someone like me who does not really believe in paying for stuff (or, in Brazilian Portuguese, a '[muquirana](https://dicionario.priberam.org/muquirana)'). However, I later discovered the option to integrate the S3 bucket with a Content Delivery Network (CDN), a global network of servers that accelerates image loading times and caches results, reducing the need for frequent payments for image requests each time an email is opened. The use of a CDN can be particularly beneficial for optimizing costs and enhancing overall performance in your email image hosting strategy.

  Overall I found this to be the best approach, and it is also very useful when some style your UX team wants does not work on an e-mail provider (yes, i'm looking at you outlook desktop app, which for some reason is still used by a lot of people).

### Conclusion

  In summary, emails present unique challenges and limitations, yet they can still exude aesthetic appeal. Now, while I made some jokes about irritating your UX team, it is imperative that you as a developer can effectively communicate these restrictions to them so that they can best create a design that is beautiful and possible to make. This collaborative approach ensures a balance between aesthetics and functionality. Explore https://mosaico.io/ for valuable, free insights into crafting impressive email designs!