Action Mailer
Phlex components excel at creating email templates, though email styling requires a different approach due to the limitations and inconsistencies of email clients. Unlike web browsers, email clients have varying levels of CSS support, making inline styles the most reliable approach.
Using with Action Mailer
Integrating Phlex components with Action Mailer is straightforward and provides the same benefits you get with web views: better performance, type safety, and Ruby’s composability.
Basic Action Mailer integration
First, create your email-specific component base class:
class Email::Base < Phlex::HTML
# Include any email-specific helpers
include Rails.application.routes.url_helpers
# Set default host for URL generation
def default_url_options
{ host: Rails.application.config.action_mailer.default_url_options[:host] }
end
end
Then use Phlex components in your mailer classes:
🔓 Unlock content
Pre-order this course to unlock this video, source code, and content. You'll also get to work with Brad to fine tune the course cirriculum.
class UserMailer < ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
def ▓▓▓▓▓▓▓▓▓▓▓▓▓(▓▓▓▓)
▓▓▓▓▓ = ▓▓▓▓
▓▓▓▓(
to: ▓▓▓▓▓.▓▓▓▓▓,
subject: 'Welcome to our application!'
) do |format|
format.▓▓▓▓ { ▓▓▓▓▓▓ ▓▓▓▓▓::▓▓▓▓▓▓▓▓▓▓▓▓.▓▓▓(▓▓▓▓▓) }
format.▓▓▓▓ { ▓▓▓▓▓▓ ▓▓▓▓▓::▓▓▓▓▓▓▓▓▓▓▓▓.▓▓▓(▓▓▓▓▓, format: :text) }
end
end
end
Creating email templates
▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓ ▓▓▓▓ ▓▓▓▓▓▓▓▓
class Email::WelcomeEmail < ▓▓▓▓▓::▓▓▓▓
def ▓▓▓▓▓▓▓▓▓▓(▓▓▓▓, format: :html)
▓▓▓▓▓ = ▓▓▓▓
▓▓▓▓▓▓▓ = format
end
def ▓▓▓▓▓▓▓▓▓▓▓▓▓
if ▓▓▓▓▓▓▓ == :html
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
else
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
end
end
private
def ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓ ▓▓▓▓▓::▓▓▓▓▓▓.▓▓▓(title: "Welcome!") do
▓▓(style: ▓▓▓▓▓▓▓▓▓▓▓▓[:heading]) { "Welcome, #{▓▓▓▓▓.▓▓▓▓}!" }
p(style: ▓▓▓▓▓▓▓▓▓▓▓▓[:paragraph]) do
▓▓▓▓▓ "Thanks for joining our application. "
▓(href: ▓▓▓▓▓▓▓▓▓▓▓▓▓, style: ▓▓▓▓▓▓▓▓▓▓▓▓[:link]) { "Get started here" }
▓▓▓▓▓ "."
end
end
end
def ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓ <<~▓▓▓▓
Welcome, #{▓▓▓▓▓.▓▓▓▓}!
Thanks for joining our application.
Get started here: #{▓▓▓▓▓▓▓▓▓▓▓▓▓}
▓▓▓▓
end
def ▓▓▓▓▓▓▓▓▓▓▓▓
{
heading: {
color: '#333333',
font_family: 'Arial, sans-serif',
font_size: '24px',
margin: '0 0 16px 0'
},
paragraph: {
color: '#666666',
font_family: 'Arial, sans-serif',
font_size: '16px',
line_height: '1.5',
margin: '0 0 16px 0'
},
link: {
color: '#007bff',
text_decoration: 'none'
}
}
end
end
Styling emails
▓▓▓▓▓ ▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓ ▓▓▓ ▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓ ▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓ ▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓ ▓▓ ▓▓▓▓ ▓▓▓ ▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓▓
▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓ ▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓
Email-specific component library
▓▓▓▓▓▓ ▓▓▓ ▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓
class Email::Layout < ▓▓▓▓▓::▓▓▓▓
def ▓▓▓▓▓▓▓▓▓▓(title: nil)
▓▓▓▓▓▓ = ▓▓▓▓▓
end
def ▓▓▓▓▓▓▓▓▓▓▓▓▓(&▓▓▓▓▓▓▓)
▓▓▓▓ do
▓▓▓▓ do
▓▓▓▓(charset: "utf-8")
▓▓▓▓(name: "viewport", content: "width=device-width, initial-scale=1.0")
▓▓▓▓▓ { ▓▓▓▓▓▓ } if ▓▓▓▓▓▓
end
▓▓▓▓(style: ▓▓▓▓▓▓▓▓▓▓▓) do
▓▓▓▓▓(style: ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓) do
▓▓ do
▓▓ do
▓▓▓▓▓▓ ▓▓▓▓▓::▓▓▓▓▓▓.▓▓▓
▓▓▓(style: ▓▓▓▓▓▓▓▓▓▓▓▓▓▓, &▓▓▓▓▓▓▓)
▓▓▓▓▓▓ ▓▓▓▓▓::▓▓▓▓▓▓.▓▓▓
end
end
end
end
end
end
private
def ▓▓▓▓▓▓▓▓▓▓▓
{
margin: '0',
padding: '0',
background_color: '#f4f4f4',
font_family: 'Arial, sans-serif'
}
end
def ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
{
width: '100%',
max_width: '600px',
margin: '0 auto',
background_color: '#ffffff'
}
end
def ▓▓▓▓▓▓▓▓▓▓▓▓▓▓
{
padding: '20px',
color: '#333333',
font_size: '16px',
line_height: '1.6'
}
end
end
class Email::Header < ▓▓▓▓▓::▓▓▓▓
def ▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓(style: ▓▓▓▓▓▓▓▓▓▓▓▓▓) do
▓(href: ▓▓▓▓▓▓▓▓, style: ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓) do
▓▓▓(
src: ▓▓▓▓▓▓▓▓▓('logo.png'),
alt: 'Company Logo',
style: ▓▓▓▓▓▓▓▓▓▓▓
)
end
end
end
private
def ▓▓▓▓▓▓▓▓▓▓▓▓▓
{
padding: '20px',
text_align: 'center',
background_color: '#ffffff',
border_bottom: '1px solid #eeeeee'
}
end
def ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
{
text_decoration: 'none'
}
end
def ▓▓▓▓▓▓▓▓▓▓▓
{
max_width: '200px',
height: 'auto',
display: 'block',
margin: '0 auto'
}
end
end
class Email::Footer < ▓▓▓▓▓::▓▓▓▓
def ▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓(style: ▓▓▓▓▓▓▓▓▓▓▓▓▓) do
p(style: ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓) do
▓▓▓▓▓ "© #{▓▓▓▓.▓▓▓▓▓▓▓.▓▓▓▓} Your Company Name. All rights reserved."
end
p(style: ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓) do
▓(href: ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓, style: ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓) { "Unsubscribe" }
▓▓▓▓▓ " | "
▓(href: ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓, style: ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓) { "Privacy Policy" }
end
end
end
private
def ▓▓▓▓▓▓▓▓▓▓▓▓▓
{
padding: '20px',
text_align: 'center',
background_color: '#f8f9fa',
border_top: '1px solid #eeeeee'
}
end
def ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
{
margin: '0 0 10px 0',
font_size: '12px',
color: '#666666',
line_height: '1.4'
}
end
def ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
{
color: '#007bff',
text_decoration: 'none'
}
end
end
Email styling best practices
- ▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓
- ▓▓▓▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓ ▓▓▓▓▓
- ▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓ ▓▓▓▓▓▓ ▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓ ▓▓▓▓ ▓▓▓▓
- ▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓
- ▓▓▓▓ ▓▓ ▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓ ▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓
- ▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓
Testing email components
▓▓▓▓ ▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓
▓ ▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓▓
require 'rails_helper'
▓▓▓▓▓.▓▓▓▓▓▓▓▓ ▓▓▓▓▓::▓▓▓▓▓▓▓▓▓▓▓▓ do
▓▓▓(:user) { ▓▓▓▓▓▓(:user, name: 'John Doe', email: 'john@example.com') }
▓▓▓(:component) { ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓.▓▓▓(▓▓▓▓) }
▓▓ 'renders welcome message' do
▓▓▓▓ = ▓▓▓▓▓▓▓▓▓.▓▓▓▓
▓▓▓▓▓▓(▓▓▓▓).▓▓ include('Welcome, John Doe!')
▓▓▓▓▓▓(▓▓▓▓).▓▓ include('Get started here')
end
▓▓ 'includes proper email structure' do
▓▓▓▓ = ▓▓▓▓▓▓▓▓▓.▓▓▓▓
▓▓▓▓▓▓(▓▓▓▓).▓▓ include('<table')
▓▓▓▓▓▓(▓▓▓▓).▓▓ include('style=')
end
end
▓▓ ▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓