Creating Forms in Ruby on Rails with Simple Form

Thomas Riboulet

Thomas Riboulet on

Creating Forms in Ruby on Rails with Simple Form

Ruby on Rails has changed how we build web applications. Early on, the framework came with some great features to help you get started and build robust applications.

However, it can still be tricky to build and handle forms. Simple Form is a great option. Let's examine what Simple Form is, why we might need it, and some real use cases.

Forms and Ruby on Rails

Ruby on Rails really simplifies building applications. Yet, it also requires your constant attention to keep your codebase streamlined and coherent. One strategy is to avoid writing code by using abstractions, for example. Code that isn't in your actual application is code you don't have to manage. That's why we like abstractions so much, right? Simple Form is just such an abstraction that simplifies building forms for web pages.

Forms are generally complex to build, even using Ruby on Rails. Each field requires attention to define the right type and attach it to the correct attribute. Simple Form eases that pain, as you don't need to find the exact type required for each field.

Let's take a form that gets a user's details, with name, username, and email fields. Consider that we have the User model with similar related attributes. With Simple Form, the form that fills in attributes looks like the following:

<%= simple_form_for @user do |f| %> <%= f.input :name, required: false %> <%= f.input :username %> <%= f.input :email %> <%= f.button :submit %> <% end %>

Note that we are not specifying the types of each field. Simple Form will pick the correct input type for each field based on the column's type.

This saves us time and keeps the code readable and easier to maintain.

Installing Simple Form

To install Simple Form in your project, add the simple_form gem to the Gemfile. Once you have run bundle install, you can use the included generator to set it up correctly for your application.

rails generate simple_form:install

A note on Bootstrap and Zurb Foundation: Both Bootstrap and Zurb Foundation 5 are supported within Simple Form. So, if you are using one of them, you can use the --bootstrap or --foundation parameter with the generator to include additional configurations during the installation. In the case of Bootstrap, this would look like the following:

rails generate simple_form:install --bootstrap

Basic Usage of Simple Form

As we pointed out earlier, Simple Form will generate a complete form mainly based on the data related to your objects. Yet, it's also important to understand that Simple Form will not only generate the appropriate field for each attribute but also provide labels and display error hints above the input fields themselves!

Let's consider a fresh new Ruby on Rails 7.x application. We'll generate a User model and build a form for it using Simple Form.

rails g model User username:string password:string email:string remember_me:boolean # and follow with running the migration of course rails db:migrate

Now we can generate a controller with the new, create, and index actions. The aim is to have something we can play with in the form.

rails g controller users
# app/users_controller.rb class UsersController < ApplicationController def new render :new, locals: { user: } end def create user = User.create(user_params) redirect_to users_path end def index render :index, locals: { users: User.all } end private def user_params params.require(:user).permit(:username, :password, :email) end end
# config/routes.rb Rails.application.routes.draw do resources :users, only: [:new, :create, :index] end

These actions require relevant views. Here is a form that handles a user signing up or being added:

<!-- app/views/users/new.html.erb --> <%= simple_form_for user do |f| %> <%= f.input :username, label: 'Your username please', error: 'Username is mandatory, please specify one' %> <%= f.input :password, hint: 'No special characters.' %> <%= f.input :email, placeholder: '' %> <%= f.input :remember_me, inline_label: 'Yes, remember me' %> <%= f.button :submit %> <% end %>

In this case, we specify a custom label and error for the username input. If no such customization is passed, it will do its best to guess what the label needs to be based on the attribute's name.

We have set a custom hint for the password field and a custom placeholder for the email field. Notice, also, the ability to specify an "inline label" instead of one located above the field (the usual default in forms).

It's also possible to disable labels, hints, and errors by passing the false value for those attributes: <%= f.input :password_confirmation, label: false %>.

We need to complement this with one more view to make it usable.

<!-- app/views/users/index.html.erb --> <ul> <% users.each do |user| %> <li><%= user.username %> (<%= %>)</li> <% end %> </ul> <p> <%= link_to 'New User', new_user_path %> </p>

You can then start the application using rails s. Head to localhost:3000/users/new to see and use the form. Let's focus on how the form differs from a classic Ruby on Rails form.

A note on validations: Simple Form can work with mandatory fields out of the box. For example, if we were to add the following presence validation in our User model:

class User < ApplicationRecord validates :username, presence:true # more code end

This will be used by Simple Form to add a little '*' next to the username field, specifying that it's mandatory. This doesn't manage the errors automatically, so you still have to handle that in the controller action. Return to the appropriate field that has errors and act on them.

About Column Types and Form Fields

As mentioned earlier, we haven't specified a type for each field. The Simple Form README has a complete list of all available input types and the defaults for each column type. Here are the most used ones:

Column TypeGenerated HTML elementComment
(passwords) stringinput[type=password]Any column with a name containing 'password'
integer, floatinput[type=number]
datetimedatetime select
timetime select


As we can see in the above table, boolean attributes will be represented, by default, as checkboxes. In many cases, that's what we'll want. But if not, there is a way to customize this in Simple Form with the as attribute, which allows you to specify if you will show radio buttons or a dropdown instead.

Here is how to specify radio buttons instead:

<%= f.input :remember_me, input_html: { value: '1' }, as: :radio_buttons %>

This will generate the following HTML:

<div class="input radio_buttons optional user_remember_me"> <label class="radio_buttons optional">Remember me</label> <input type="hidden" name="user[remember_me]" value="" autocomplete="off" /> <span class="radio"> <label for="user_remember_me_true"> <input value="true" class="radio_buttons optional" type="radio" name="user[remember_me]" id="user_remember_me_true" />Yes </label> </span> <span class="radio"> <label for="user_remember_me_false"> <input value="false" class="radio_buttons optional" readonly="readonly" type="radio" name="user[remember_me]" id="user_remember_me_false" />No </label> </span> </div>

It's impressive that such a small piece of Ruby code gets you such a complete piece of HTML!


From the previous example, we can see how Simple Form generates a lot of HTML for each field. That includes the HTML for the input field and a wrapper div around the label and input field. We can customize those by specifying a custom class and id using the input_html and wrapper_html attributes. Here is an example.

<%= simple_form_for @user do |f| %> <%= f.input :username, wrapper_html: { class: 'username' }, input_html { id: 'username' } %> <% end %>

That's also a way to set attributes for the related HTML, such as maxlength or value. Let's take the password field from our user form. We can limit the size and length of the field with the maxlength attribute.

<%= f.input :password, input_html: { maxlength: 20 } %>

Custom Inputs and Additional Options

Simple Form is a Ruby library that prepares HTML nodes for you. It comes with a whole set of fields, but you can also add your own. To do so, you can create classes inheriting from Simple Form's classes. Let's consider defining a custom social network input field with a little '@' prefix in front of the actual field.

# app/inputs/social_handle_input.rb class SocialHandleInput < SimpleForm::Inputs::Base def input(wrapper_options) merged_input_options = merge_wrapper_options(input_html_options, wrapper_options) "@ #{@builder.text_field(attribute_name, merged_input_options)}".html_safe end end

Now we can use it in the following way:

<%= f.input :network_handle, as: :social_handle %>

Note that, if your User model doesn't have a network_handle attribute, you must add it through a migration or cheat with an attr_accessor in the model.

i18n Support

As we've mentioned, building forms is often a pain point. But things get even more painful when it comes to websites and applications that support multiple languages. Simple Form, thankfully, follows Ruby on Rails standards. You can use a simple_form key in the local files to define translations for all labels, hints, placeholders, prompts, etc.

Here is a little example:

en: simple_form: labels: user: username: 'User name' password: 'Password' hints: user: username: 'User name to sign in.' password: 'No special characters, please.' placeholders: user: username: 'Your username' password: '****' include_blanks: user: age: 'Rather not say' prompts: user: role: 'Select your role'

Value Objects

Sometimes, we might use custom, non-ActiveRecord classes to instantiate the main object a form relies upon. This can be done to compose data from multiple models into a synthetic one for either business logic reasons or to gather several attributes into a single object.

Whatever the reason, you can still rely on Simple Form to build forms for that kind of object. To do so, the object class needs to implement, in the best case, three methods: to_model, to_key, and persisted?.

The to_model method will point to the object itself:

def to_model self end

The to_key allows us to point to the identifier attribute for the object — usually, that means an id attribute named:

def to_key id end

Finally, the persisted? method is there to tell Simple Form if the object is directly persisted or not.

def persisted? false end

If that method isn't present, the f.submit helper isn't usable.

There is a faster way, though — including the ActiveModel::Model module in the class:

# app/models/account.rb class Account include ActiveModel::Model attr_accessor :id, :company_name end

And let's use it through the User model.

# app/models/user.rb class User < ApplicationRecord attr_accessor :network_handle def account @account ||= 1, company_name: "Acme Inc.") end def company_name account.company_name end end

We can then add a field for the company name within the user form:

<%= f.input :company_name %>

Of course, this attribute will not get saved in a table. However, the same concept can be applied to compose a value object with attributes pulled from several models.

Wrapping Up

In this post, we've seen that Simple Form is easy to install and integrate into your Ruby on Rails application. Simple Form not only takes care of the details of each field in your form, but also generates complex and complete HTML so your form can be styled and shaped with ease.

I encourage you to dive into Simple Form's documentation to see how powerful it can be.

Happy coding!

P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, subscribe to our Ruby Magic newsletter and never miss a single post!

Thomas Riboulet

Thomas Riboulet

Our guest author Thomas is a Consultant Backend and Cloud Infrastructure Engineer based in France. For over 13 years, he has worked with startups and companies to scale their teams, products, and infrastructure. He has also been published several times in France's GNU/Linux magazine and on his blog.

All articles by Thomas Riboulet

Become our next author!

Find out more

AppSignal monitors your apps

AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!

Discover AppSignal
AppSignal monitors your apps