How to render HTML email inside a template of Phoenix app


I’m working on an app that will display users emails. My question is in regards to render an HTML email inside the app.

So the user would log in, click on an email and email gets displayed like it does in any email client.

But in my app, the HTML email content also inherits the CSS of the entire site.

Is there a way to render the HTML email almost in isolation and prevent it from applying the site’s CSS?

Thank you,


1 Like

Try the Smoothie library.

1 Like

I’ve achieved it with an iframe. Add a route with an empty layout, which simply renders the content of an email.

You might also use multiple layouts… and select which one to use when rendering html email.

The problem with emails is that CSS has to be inline (f.ex. <span style="font-width: ...>) and not in separate CSS files that are referred to through classes – and I don’t think Phoenix does that conversion automatically.

1 Like

Maybe not Phoenix, but the browser should :slight_smile:

1 Like

How though? If you have this in your email:

<span class="subtitle">Hello, please buy our stuff</span>

And the CSS class is in another file – which is not linked inside the email message itself – then how would an email client know what to render?

I mean, the OP can make their app use the classic techniques (CSS classes in the HTML elements + separate CSS files) but I don’t think they will achieve 100% parity with how email clients actually display content.

1 Like

An alternative to inline the css in each line is to use <head><style>...</style></head> in the email document.

So using the <style> tag with css classes prefixed with something the website doesn’t use will allow to keep “parity” in the rendering between the email client and the web app. Bear in mind that email clients don’t keep parity in the rendering between them, thus it may be a good idea to also include the css reset rules inside the <style> tag.

Yes, but that still means that the CSS must be inside the same HTML file that is being sent as email message. There are ways to just refer to external resources – that’s how email marketing campaigns work; the companies know you actually opened the message (if the invisible pixel gets loaded) – but many email clients will refuse to load external CSS so the safest bet is to use one of the few possible approaches of inlining the CSS inside the HTML itself.

Exactly the same as for the inline css. So or I am not getting your point or your are not getting mine :wink:

I actually do get it and said as much. I simply added that whether you (a) have all CSS in a <head> tag or (b) have all styles inside the HTML elements themselves (via the style="..." attribute) it makes no difference – you still can’t use an external CSS file.

1 Like

Hello again,

Thank you for your replies and thoughts everyone.

I think I might have asked my original question poorly. So please allow me another shot at it.

So, what I’ve done is created a new, stock Phoenix application that has an emails resource and a string attribute. In that string attribute I’ve stored the complete HTML content of an HTML email that I received. When I render the show page for the email it looks like this:

But if I disable the stock CSS that was included in the project the HTML email generates like this:

The CSS that come with the LinkedIn email gets applied to the rest of the site.

What I would like to know is if anyone has any idea on how to render BOTH the site as is with its CSS and then also render the HTML along with its inline CSS without the site or the email overriding each other’s design.

Hope that’s a little more clear and I also hope someone might have an idea on how to do this.



I believe you have something similar in your template file:

<div class="email-content">
  <%= @email_html %>

and this is how you leak the CSS into your entire page. To avoid that, you should use an iframe. Iframe helps you embedding a site in your site in complete isolation. To do that, you will need to do a couple of things.

  1. Add new route, for example get "/email/iframe/, MyController, :email_iframe
  2. Add email_iframe/2 to the MyController file.
  3. Add whatever code you need to retrieve the email record and make assignments.
  4. Put a layout with Phoenix’s put_layout/2, for example conn = put_layout(conn, "email_iframe.html").
  5. Create the new layout with following content:
<%= @inner_content %>
  1. And lastly update your initial template to be something like this:
<div class="email-content">
  <iframe src="<%= Routes.email_iframe_url(@conn, %>">

I hope this will help you.


@andrejsm your solution totally worked! Wow. I am so happy. And excited. WOW!

Thank you so much for your insights.

This is how everything renders now after I implemented your suggestions.

If we’re ever in the same area, dinner’s on me.

Thank you again.