Adding a simple mail form to a Rails app is well-documented throughout the web. However, there's not as much documentation for a mail form on a single-page app that submits with Ajax. This tutorial will address a quick and straightforward way to put a mail form on a single-page web site.

First let's get an app up an running. I'm using rvm for my versioning, though it's not necessary.

 mkdir contactForm
 cd contactForm
 mkdir contact_form
 cd contact_form
 rvm use ruby-2.4.0
 gem install rails
 rails new .

Make your initial git commit. Rails 5 automatically initializes the repository.

Let's add the gems we'll need to the Gemfile.

gem 'bootstrap-sass', '~> 3.3', '>= 3.3.6'
gem 'mail_form'
gem 'jquery-rails', '~> 4.1', '>= 4.1.1'
gem 'dotenv-rails', groups: [:development, :test]
bundle install

To make development more secure we're using the dotenv gem; add .env to the .gitignore file. Now make the .env file in the root directory. In that file add your email credentials; something like this. You will be protected from erroneously uploading sensitive credentials to GitHub, which can cause a huge headache .


Make sure your app/assets/javascripts/application.js file looks something like this. We'll need the jQuery for the Ajax rendering.

//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require bootstrap
//= require_tree .

Start or restart the rails server.

Now let's set up a basic, very basic, one-page app with an index page which will be our home page and can be easily customized. The following code in the Rails console will generate the Home controller with an index action.

rails g controller Home index

Go to app/views/home/index.html.erb and replace the existing boilerplate with this code.

<h1>This is my one page app!</h1>
  <div id="contact">
    <%= render 'contact_form' %>

In the app/views/home folder make a _contact_form.html.erb partial and insert the following code. Notice the remote: true option in the form method helper which enables Ajax in Rails.

<%= form_for @contact, url: home_index_path, remote: true do |f| %>
    <div class="col-md-6">
        <%= f.label :name %></br>
        <%= f.text_field  :name, required: true, class: "contact-form-text-area" %></br>

        <%= f.label :email %></br>
        <%= f.text_field :email, required: true, class: "contact-form-text-area" %></br>

      <%= f.label :message %></br>
      <%= f.text_area :message, rows: 8, cols: 40, required: true, class: "contact-form-text-area",
                        placeholder: "Send a message to Glenn."%></br>

      <div class= "hidden">
        <%= f.label :nickname %>
        <%= f.text_field :nickname, :hint => 'Leave this field blank!' %>

      <%= f.submit 'Send Message', class: 'btn btn-primary' %>
  <% end %>
  <div class="col-md-6" id="flash-message">
    <%= render 'flash' %>

Create another partial, _flash.html.erb, in the home folder which is going to hold our code for the flash messages. Insert the following into that file.

<% flash.each do |message_type, message| %>
    <%= content_tag(:div, message, class: "alert alert-#{message_type}") %>
<% end %>

Again in the same home folder, make a create.js.erb file which will be called with the create action in the controller.

// Test for ajax success
console.log("This is the create.js.erb file");
// Render flash message
$('#contact').html("<%= j render 'contact_form' %>");
$('#flash-message').html("<%= j render 'flash' %>").delay(3000).fadeOut(4000);

Go to the config/routes.rb file and add the following to set up the routes and the actions for the home page that will contain the mail form.

root      'home#index'
resources :home, only: [:index, :new, :create]

In the home_controller.rb file replace what's there with the following.

class HomeController < ApplicationController
  def index
    @contact =[:home])

  def create
    @contact =[:home])
    @contact.request = request
    respond_to do |format|
      if @contact.deliver
        # re-initialize Home object for cleared form
        @contact =
        format.html { render 'index'}
        format.js   {[:success] = @message = "Thank you for your message. I'll get back to you soon!" }
        format.html { render 'index' }
        format.js   {[:error] = @message = "Message did not send." }

The index action will render our one-page app and the create action will handle the creation and submission of the mail form. Now let's set up our model. In the models folder make a home.rb file and add the following code substituting your email credentials in the headers method. Since we are not storing our form data in a database, the model does not inherit from ActiveRecord::Base

class Home < MailForm::Base
  attribute :name,      :validate => true
  attribute :email,     :validate => /\A([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})\z/i
  attribute :message
  attribute :nickname,  :captcha  => true

  # Declare the e-mail headers. It accepts anything the mail method
  # in ActionMailer accepts.
  def headers
      :subject => "Contact You",
      :to => "",
      :from => %("#{name}" <#{email}>)

Let's add some basic styling to our home.scss file.

@import "bootstrap-sprockets";
@import "bootstrap";

body {
  background-color: black;
  color: white;
.contact-form-text-area {
  color: black;
.hidden { display: none; }
.alert-error {
background-color: #f2dede;
border-color: #eed3d7;
color: #b94a48;
text-align: left;
font-size: .8em;

.alert-alert {
background-color: #f2dede;
border-color: #eed3d7;
color: #b94a48;
text-align: left;
font-size: .8em;

.alert-success {
background-color: #dff0d8;
border-color: #d6e9c6;
color: #468847;
text-align: center;
font-size: .8em;

.alert-notice {
background-color: #dff0d8;
border-color: #d6e9c6;
color: #468847;
text-align: left;
font-size: .8em;

At localhost:3000 we should see our nice mail form.

rails s

Now we'll configure our Rails app to actually send a message. In the config/environments/development.rb file add the following code. Enter the user_name and password credentials exactly as noted as they are imported via the .dotenv gem. NOTE: If using Google mail you'll probably need to reduce the security settings on your Google mail account for your development Rails app to send a message from your development server.

# For mail_form gem
  config.action_mailer.perform_deliveries = true
  config.action_mailer.raise_delivery_errors= true
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
    address:              '',
    port:                      587,
    domain:                 '',
    user_name:           ENV["GMAIL_USERNAME"],
    password:              ENV["GMAIL_PASSWORD"],
    authentication:      'plain',
    enable_starttls_auto: true  }

Now if you go back to your home page and fill in a test message, your form will send.

Success! You now have a mail form in Rails.