Skip
Arish's avatar

41. Form Helpers


Rails Form Helpers

Rails provides powerful form helpers that make building forms easy and secure. They automatically handle CSRF protection, model binding, and error display.

form_with Helper

The modern way to create forms in Rails:

erb
1<!-- Resource-based form (for model) -->
2<%= form_with model: @article do |form| %>
3  <%= form.text_field :title %>
4  <%= form.text_area :body %>
5  <%= form.submit %>
6<% end %>
7
8<!-- URL-based form -->
9<%= form_with url: search_path, method: :get do |form| %>
10  <%= form.text_field :query %>
11  <%= form.submit "Search" %>
12<% end %>

Generated HTML

html
1<form action="/articles" method="post" data-turbo="true">
2  <input type="hidden" name="authenticity_token" value="..." />
3  <input type="text" name="article[title]" id="article_title" />
4  <textarea name="article[body]" id="article_body"></textarea>
5  <input type="submit" value="Create Article" />
6</form>

Common Form Fields

Text Fields

erb
1<%= form_with model: @user do |f| %>
2  <!-- Basic text field -->
3  <%= f.text_field :name %>
4  
5  <!-- With placeholder and class -->
6  <%= f.text_field :name, placeholder: "Enter name", class: "form-control" %>
7  
8  <!-- Email field -->
9  <%= f.email_field :email %>
10  
11  <!-- Password field -->
12  <%= f.password_field :password %>
13  
14  <!-- Hidden field -->
15  <%= f.hidden_field :referrer, value: "homepage" %>
16  
17  <!-- Phone field -->
18  <%= f.telephone_field :phone %>
19  
20  <!-- URL field -->
21  <%= f.url_field :website %>
22  
23  <!-- Number field -->
24  <%= f.number_field :age, min: 0, max: 120 %>
25  
26  <!-- Range field -->
27  <%= f.range_field :rating, min: 1, max: 5 %>
28<% end %>

Text Areas

erb
1<%= f.text_area :body %>
2<%= f.text_area :body, rows: 10, cols: 50 %>
3<%= f.text_area :body, size: "50x10" %>
4<%= f.text_area :description, class: "rich-editor", data: { controller: "editor" } %>

Select Fields

erb
1<!-- Basic select -->
2<%= f.select :category, ["Technology", "Sports", "Music"] %>
3
4<!-- With blank option -->
5<%= f.select :category, ["Technology", "Sports", "Music"], 
6    include_blank: "Select a category" %>
7
8<!-- With prompt -->
9<%= f.select :category, ["Technology", "Sports", "Music"], 
10    prompt: "Choose one..." %>
11
12<!-- From collection -->
13<%= f.collection_select :category_id, Category.all, :id, :name %>
14
15<!-- With selected value -->
16<%= f.select :status, [["Draft", "draft"], ["Published", "published"]], 
17    selected: "draft" %>
18
19<!-- Grouped options -->
20<%= f.grouped_collection_select :city_id, Country.all, :cities, :name, :id, :name %>

Checkboxes and Radio Buttons

erb
1<!-- Single checkbox -->
2<%= f.check_box :terms_accepted %>
3<%= f.label :terms_accepted, "I accept the terms" %>
4
5<!-- Checkbox with custom values -->
6<%= f.check_box :active, {}, "yes", "no" %>
7
8<!-- Multiple checkboxes from collection -->
9<%= f.collection_check_boxes :tag_ids, Tag.all, :id, :name do |b| %>
10  <div class="checkbox">
11    <%= b.check_box %>
12    <%= b.label %>
13  </div>
14<% end %>
15
16<!-- Radio buttons -->
17<%= f.radio_button :gender, "male" %>
18<%= f.label :gender_male, "Male" %>
19<%= f.radio_button :gender, "female" %>
20<%= f.label :gender_female, "Female" %>
21
22<!-- Radio from collection -->
23<%= f.collection_radio_buttons :role_id, Role.all, :id, :name do |b| %>
24  <div class="radio">
25    <%= b.radio_button %>
26    <%= b.label %>
27  </div>
28<% end %>

Date and Time Fields

erb
1<!-- Date field (HTML5) -->
2<%= f.date_field :birth_date %>
3
4<!-- Time field -->
5<%= f.time_field :start_time %>
6
7<!-- Datetime field -->
8<%= f.datetime_local_field :published_at %>
9
10<!-- Date select (Rails dropdowns) -->
11<%= f.date_select :birth_date, start_year: 1900, end_year: Date.today.year %>
12
13<!-- Time select -->
14<%= f.time_select :start_time, minute_step: 15 %>
15
16<!-- Datetime select -->
17<%= f.datetime_select :event_at %>

File Uploads

erb
1<!-- File field -->
2<%= f.file_field :avatar %>
3
4<!-- Multiple files -->
5<%= f.file_field :documents, multiple: true %>
6
7<!-- With accept type -->
8<%= f.file_field :image, accept: "image/*" %>

Labels

erb
1<%= f.label :name %>
2<!-- <label for="user_name">Name</label> -->
3
4<%= f.label :name, "Full Name" %>
5<!-- <label for="user_name">Full Name</label> -->
6
7<%= f.label :name, class: "form-label" %>
8
9<%= f.label :name do %>
10  <strong>Name</strong> <small>(required)</small>
11<% end %>

Form Options

erb
1<%= form_with model: @article, 
2    local: true,           # Disable Turbo/AJAX
3    url: custom_path,      # Custom URL
4    method: :patch,        # HTTP method
5    class: "my-form",      # CSS class
6    id: "article-form",    # HTML ID
7    data: { turbo: false }, # Disable Turbo
8    html: { novalidate: true } do |f| %>
9<% end %>

Nested Attributes

For associated models:

ruby
1# Model
2class Article < ApplicationRecord
3  has_many :comments
4  accepts_nested_attributes_for :comments, 
5    allow_destroy: true, 
6    reject_if: :all_blank
7end
erb
1<%= form_with model: @article do |f| %>
2  <%= f.text_field :title %>
3  
4  <h3>Comments</h3>
5  <%= f.fields_for :comments do |comment_form| %>
6    <div class="comment-fields">
7      <%= comment_form.text_area :body %>
8      <%= comment_form.check_box :_destroy %>
9      <%= comment_form.label :_destroy, "Remove" %>
10    </div>
11  <% end %>
12  
13  <%= f.submit %>
14<% end %>

Displaying Validation Errors

erb
1<%= form_with model: @user do |f| %>
2  <% if @user.errors.any? %>
3    <div class="error-messages">
4      <h2><%= pluralize(@user.errors.count, "error") %> prevented saving:</h2>
5      <ul>
6        <% @user.errors.full_messages.each do |message| %>
7          <li><%= message %></li>
8        <% end %>
9      </ul>
10    </div>
11  <% end %>
12  
13  <div class="field <%= 'has-error' if @user.errors[:name].any? %>">
14    <%= f.label :name %>
15    <%= f.text_field :name %>
16    <% if @user.errors[:name].any? %>
17      <span class="error"><%= @user.errors[:name].first %></span>
18    <% end %>
19  </div>
20<% end %>

Form Styling with Tailwind

erb
1<%= form_with model: @user, class: "space-y-6" do |f| %>
2  <div>
3    <%= f.label :name, class: "block text-sm font-medium text-gray-700" %>
4    <%= f.text_field :name, 
5        class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" %>
6  </div>
7  
8  <div>
9    <%= f.label :email, class: "block text-sm font-medium text-gray-700" %>
10    <%= f.email_field :email, 
11        class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm" %>
12  </div>
13  
14  <%= f.submit "Save", 
15      class: "inline-flex justify-center rounded-md border border-transparent bg-indigo-600 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-indigo-700" %>
16<% end %>

Forms are a core part of Rails applications - master these helpers!