September 15, 2022

Adding Contact Forms in Rails 7 with Mail_Form Gem

This article will walk through creating a simple contact form in your Rails 7 app such as on the Railscoder site. We'll be using the Mail-Form Gem to make this easy.

First. head over to Ruby Gems and get the gemfile for Mail_Form. Then add it to your Gemfile declararion and run bundle.

# Gemfile

gem 'mail_form', '~> 1.9'
# shell

$ bundle install

The next thing you will want to do is generate the contact form using the builder in Mail_Form.

I am naming my form "Contact" as you will see in this example below.

# shell

$ rails generate mail_form Contact name:string email: string message:text

You will see it generate a contact.rb file in your models.

Open that file to add validations and change the headers.

When you first open the file, it looks like this:

# app/models/contact.rb

class Contact < MailForm::Base
  attribute :name
  attribute :email
  attribute :message

  def headers
    { to: "[email protected]" }

We need to make some changes to get the validations in there as well as send the email to the correct place. In the case below, I am validating that the form has a name, a valid email from a regex, and a message. I am also using the captcha validation from the gem called "nickname" that will ensure against bots filling out the form and spamming me. Just remember that when we create our views - we will need a hidden field on our form that will trick bots but real humans will never see it.

# app/models/contact.rb

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

  def headers
      to: "[email protected]", # change this to be the email you want sent to
      subject: "Railscoder Contact Form",
      from: "[email protected]",  # change this to be the email it is coming from
      reply_to: %("#{name}" <#{email}>) 

Now, we need to generate the controller since that is not handled with the generator from the gem.

# shell

$ rails generate controller contacts

Now, let's define the actions for that controller.

# app/controllers/contact_controller.rb

class ContactsController < ApplicationController
    def new
        @contact =

    def create
        @contact =[:contact])
        @contact.request = request
        if @contact.deliver
      redirect_to action: :sent
  [:error] = 'Could not send message'
            render :new, status: :unprocessable_entity

  def sent



Now we create the views. In the app/views/contacts folder, we will need views for the corresponding actions we created in the Contacts Controller. So create the files "new.html.erb" and "sent.html.erb".

I will first create the new.html.erb file and add the code and html with the form. And remember, we need that hidden field for "nickname" to trick bots.

# app/views/contacts/new.html.erb

<div class="px-8 py-6 rounded-md shadow bg-white">
    <h1 class="text-4xl text-center">Contact Railscoder</h1>
    <p>I'd love to hear from you. Send me a message below.</p>

    <%= form_for @contact do |form| %>

    <% if @contact.errors.any? %>
        <div id="error_explanation" class="bg-rose-200 pt-3 pb-1 mb-4 px-4 rounded-md">
            <div class="font-bold text-rose-700 text-sm pb-4"><%= pluralize(@contact.errors.count, "error") %> prohibited this page from being saved:</div>

            <ul class="pb-0 mb-0">
                <% @contact.errors.each do |error| %>
                    <li class="text-rose-600 pl-4 text-sm"><%= error.full_message %></li>
                <% end %>
    <% end %>

    <div class="field mb-6">
        <%= form.label :name %>
        <%= form.text_field :name %>

    <div class="field mb-6">
        <%= form.label :email %>
        <%= form.email_field :email %>

    <div class="field">
        <%= form.label :message %>
        <%= form.text_area :message %>

    <div class="hidden">
        <%= form.label :nickname %>
        <%= form.text_field :nickname %>

    <div class="actions flex justify-end">
        <%= form.submit "Send Message" %>
    <% end %>

And then we need the sent view file. This is the view that gets delivered back to the user once the fill out and submit the form. You can put anything you like here but I kept it simple with a confirmation that the user's message was sent.

#  app/views/contacts/sent.html.erb

<div class="rounded bg-white shadow p-6">
    <h1>Message Sent!</h1>
    <p>Nice job! You successfully sent me a message. If needed, I will get back to you as soon as possible. In the meantime, check out all my latest articles above.</p>

Style all the views how you see fit. I am using Tailwind and some really basic styling for this example.

Now we need to add our routes or none of this will work. I added an aliased route to simplify the view name in the browser title bar.

# config/routes.rb

resources :contacts, only: [:new, :create ]
get '/contacts', to: 'contacts#new', as: 'contact'
get 'contacts/sent'


Sign Up or Login to comment.

Master Jan Masteryourownway posted:

It didn't work out. I have a "contact" page in the navbar where should appear the contact form where I should set the route then and the view?