"Emails on Rails" | HTML, CSS and Previews


Emails are omnipresent on the Web, however, they don't draw in much attention at first when it comes to Web development. So, when you finally hit a task related to emails, it is usually a time for some WTFs and How-Tos moments.

We already got used to nice-looking emails, which means having a decent amount of HTML and CSS in there. Obviously, it requires more development effort to compose and maintain all that code, but also it might result in a huge pain when failed to set up a proper development environment.

Luckily, Rails & Friends got you covered ;)

Apart from the typical configuration for Action Mailer in "test" and "development" environments, let's spot other areas of concern mainly related to dealing with HTML and CSS in emails.

Styles

Many email clients have a limited environment for CSS applications, so the general rule of thumb is to write CSS rules inline within the style attribute of each tag. While it is possible to do it manually, the manual method quickly becomes impractical when given a more demanding email design.

There are a couple of solutions to mitigate this problem. The one I used was the premailer gem with its convenient wrapper the premailer-rails. It will inline needed styles for you, allowing you to write CSS separately and with all the goodies like SCSS.

Previews

Well, we need to see how our emails look like, right. Emails got delivered differently than regular HTML pages, so it is not right away clear how to preview an email. That is where you may shoot yourself in the foot accidentally.

Long ago, a college of mine suggested using the mailcatcher gem to preview my emails. At that time, my HTML layout was quite simple thus, it did the trick.

The next time I had a more complex layout, this approach did not work well, pushing me to look for better options. Mailcatcher is an excellent tool for testing emails, and I am still using it, but not just for the sake of a preview. Having to send an actual email each time you tweaked markup or styles is overkill, especially when the email is a result of some complex action in your application with lots of data involved.

As it turned out, Rails has a built-in feature precisely for that, and, regarding the value it brings into development process, it probably deserves to be better known.

There is the ActionMailer::Preview class which you can use as a base to compose previews for your emails. The official description is rather introductory at this point, leaving developers with a bit of consideration on how to use it.

Usually, it is not as simple as pulling a random record from a database, as it is shown in the example from the documentation.

class UserMailerPreview < ActionMailer::Preview
  def welcome_email
    UserMailer.with(user: User.first).welcome_email
  end
end

Often multiple records are involved in an email composition, and some may be required to have a specific state, which is not easy to constantly have available in a database. Going this way also means that other developers in a team would somehow have to maintain similar states in their databases just to be able to work with emails too, potentially causing "works on my machine" type of issues.

I think it makes more sense to use the same approach in email previews as we do in our test, meaning - automatically generate all data needed for a preview as a setup stage before each preview.

Also, I would prefer to run previews in the Rails "test" environment rather than in the "development" one. Mainly because I already have all the things configured for such a case in there. Same factories can be used to generate records needed for an email preview, and you don't have to worry about those records polluting your development database because the database cleaner will wipe them away with the next iteration.

Described may then result in a slightly different example.

class UserMailerPreview < ActionMailer::Preview
  def welcome_email
    UserMailer.with(
      user: FactoryBot.create(:user)
    ).welcome_email
  end
end

It looks like email previews suit perfectly for the "test" environment. By default, they are even expected to be located under the test directory. What surprised me is that out-of-the-box previews are only enabled in the "development". To do the same for the "test" environment, you must set a configuration option in the config/environment/test.rb like this:

config.action_mailer.show_previews = true

All that is left now is to start the server with RAILS_ENV=test and checkout our emails in a browser.

Nov 10, 2021