RoR Action Mailer

Action Mailer lets your Rails application send emails. It works similarly to controllers — you write a mailer class with methods (like actions), create corresponding view templates, and call the mailer from your controllers or background jobs. Rails handles composing and sending the email for you.

How Action Mailer Fits In

Controller (like ArticlesController)         Mailer (like UserMailer)
  handles HTTP requests                        handles email sending
  has actions (index, show, create)            has mail methods (welcome, reset_password)
  has views (index.html.erb)                   has views (welcome.html.erb)
  returns HTML response                        returns email message

Generate a Mailer

rails generate mailer UserMailer welcome_email password_reset

This creates:

app/mailers/user_mailer.rb
app/views/user_mailer/welcome_email.html.erb
app/views/user_mailer/welcome_email.text.erb
app/views/user_mailer/password_reset.html.erb
app/views/user_mailer/password_reset.text.erb
spec/mailers/user_mailer_spec.rb

Writing a Mailer

app/mailers/user_mailer.rb

class UserMailer < ApplicationMailer
  default from: "noreply@myapp.com"

  def welcome_email(user)
    @user = user
    @login_url = new_user_session_url

    mail(
      to:      @user.email,
      subject: "Welcome to MyApp, #{@user.name}!"
    )
  end

  def password_reset(user)
    @user = user
    @reset_url = edit_user_password_url(reset_password_token: user.reset_password_token)

    mail(
      to:      @user.email,
      subject: "Reset your MyApp password"
    )
  end

  def order_confirmation(order)
    @order = order
    @user  = order.user

    mail(
      to:      @user.email,
      subject: "Order ##{@order.id} Confirmed"
    )
  end
end

Email View Templates

Every mailer method has two views — HTML and plain text. Always provide both so email clients that block HTML still receive readable content.

app/views/user_mailer/welcome_email.html.erb

<h1>Welcome to MyApp, <%= @user.name %>!</h1>

<p>Your account is ready. You can log in at any time:</p>

<p>
  <a href="<%= @login_url %>">Log In to MyApp</a>
</p>

<p>If you have any questions, reply to this email.</p>

<p>— The MyApp Team</p>
app/views/user_mailer/welcome_email.text.erb

Welcome to MyApp, <%= @user.name %>!

Your account is ready. Log in at:
<%= @login_url %>

If you have any questions, reply to this email.

— The MyApp Team

Sending Email from a Controller

class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
    if @user.save
      UserMailer.welcome_email(@user).deliver_later   ← async (recommended)
      redirect_to root_path, notice: "Check your inbox for a welcome email!"
    else
      render :new, status: :unprocessable_entity
    end
  end
end
deliver_later  → queues email as a background job (does not block the request)
deliver_now    → sends email immediately (blocks the request until sent)

Always use deliver_later in production. Email sending can take seconds and should never slow down a user's page load.

Configuring the Mail Server

Configure how Rails sends email in your environment files:

config/environments/development.rb

config.action_mailer.delivery_method = :letter_opener
# Shows emails in the browser instead of actually sending them
# gem "letter_opener" required

config/environments/production.rb

config.action_mailer.delivery_method   = :smtp
config.action_mailer.smtp_settings = {
  address:              "smtp.sendgrid.net",
  port:                 587,
  domain:               "myapp.com",
  user_name:            "apikey",
  password:             ENV["SENDGRID_API_KEY"],
  authentication:       :plain,
  enable_starttls_auto: true
}
config.action_mailer.default_url_options = { host: "myapp.com", protocol: "https" }

Email Preview in Development

Rails includes a mailer preview feature. Generate previews to see exactly how your emails look before sending:

test/mailers/previews/user_mailer_preview.rb

class UserMailerPreview < ActionMailer::Preview
  def welcome_email
    user = User.first || FactoryBot.build(:user)
    UserMailer.welcome_email(user)
  end

  def password_reset
    user = User.first
    UserMailer.password_reset(user)
  end
end

Visit http://localhost:3000/rails/mailers/user_mailer/welcome_email to see the rendered email in your browser.

Email Attachments

def invoice_email(order)
  @order = order
  pdf_data = InvoiceGenerator.generate(@order)

  attachments["invoice-#{@order.id}.pdf"] = {
    mime_type: "application/pdf",
    content:   pdf_data
  }

  mail(to: @order.user.email, subject: "Your Invoice")
end

Action Mailer Flow

User registers on your site
        |
        v
UsersController#create
  @user.save
  UserMailer.welcome_email(@user).deliver_later
        |
        v
Job queued in ActiveJob (background)
        |
        v
Background worker picks up the job
  Calls UserMailer.welcome_email(@user)
        |
        v
Mailer builds the email:
  Renders welcome_email.html.erb + .text.erb
  Sets to:, subject:, from:
        |
        v
Rails hands email to SMTP server
        |
        v
SMTP delivers email to user's inbox

Testing Mailers

spec/mailers/user_mailer_spec.rb

RSpec.describe UserMailer, type: :mailer do
  describe "#welcome_email" do
    let(:user)  { create(:user, name: "Alice", email: "alice@example.com") }
    let(:mail)  { UserMailer.welcome_email(user) }

    it "renders the subject" do
      expect(mail.subject).to eq("Welcome to MyApp, Alice!")
    end

    it "sends to the user's email" do
      expect(mail.to).to include("alice@example.com")
    end

    it "sends from the default address" do
      expect(mail.from).to include("noreply@myapp.com")
    end

    it "includes the user's name in the body" do
      expect(mail.body.encoded).to include("Alice")
    end
  end
end

Action Mailer makes transactional email — welcome messages, confirmations, password resets, invoices — straightforward to build, preview, test, and deliver reliably in production.

Leave a Comment

Your email address will not be published. Required fields are marked *