Rails: form_with vs. form_for vs. form_tag

Scott Espinosa
3 min readJul 13, 2020

To generate forms on view pages when using Rails, there are three main ways to make them. While learning Rails, I learned the older ways using form_tag and form_for. The main difference between these is that form_tag can create a form without having to use a model and form_for relies on a model to generate the form for (makes sense, right?).

The general syntax for form_tag (along with adding a class) would be:

<%= form_tag user_path, class: "form-signin" do %>  <%= label_tag :username %>
<%= text_field_tag :username %>
<%= submit_tag %>
<% end %>

The general system for form_tag in association with a user model (along with adding a class) would be:

<%= form_for @user, html: { class: "form-signin" } do |f| %>  <%= f.label :username %>
<%= f.text_field :username %>
<%= f.submit %>
<% end %>

When using form_for, we also have access to using form builders, which are not available for form_tag. Because of this syntax and other various reasons, form_for is sometimes the preferred way of generating a form.

However when Rails 5.1 was released, both of these methods are soft-deprecated, meaning that they are in the process of being replace by a newer method, which is form_with. The benefit of form_with is that it’s basically a union of both form_for and form_tag in which form builders are available to use and can generate forms with or without a model. The general syntax for form_with be as below:

Without a model:<%= form_with url: user_path, class: "form-signin" do |f| %>  <%= f.label :name %>
<%= f.text_field :name%>
<%= f.submit %>
<% end %>With a model:<%= form_with model: @user, class: "form-signin" do |f| %> <%= f.label :name %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>

As seen with the code above, the only difference in the syntax is either to specify the url path when not using a model or specifying the model when using one. Before form_with was released, we would have to use either form_tag or form_for, which each have pretty different syntax. Something else to note is with form_for, adding a class to the form itself has a specific syntax in order to assign it. Now with form_with, the syntax is a lot simpler.

Another advantage with using form_with is that form fields do not have to correspond to the model attributes. For example, if a user class only has an attribute for a username (as a string), the code below would result in an error since location (as a string) is not one of attributes for the user:

<%= form_for @user, html: { class: "form-signin" } do |f| %>  <%= f.label :username %>
<%= f.text_field :username %>
<%= f.label :location %>
<%= f.text_field :location %>
<%= f.submit %><% end %>

However, form_with actually allows fields to be generated that do not correspond to model attributes. So with whatever is input into the submit form, the data would be available to use within params, if needed. While form builders are available to use, form_with also allows mixing with other form builders and other methods from FormTagHelpers. Therefore, the code below would actually generate a form:

<%= form_with model: @user, class: "form-signin" do |f| %>  <%= f.label :name %>
<%= f.text_field :name %>
<%= label_tag :location %>
<%= text_field_tag :location %>
<%= f.submit %><% end %>

While I only touched a few features of form_with, there are many other things that form_with gives access to. But with the features I reviewed here, you can see that form_with provides a lot more flexibility in one form method instead of having the separate two different syntaxes with form_tag and form_for. Happy Form Building!

Everyone gets a form!

Resources:

  1. “Action View Form Helpers,” RailsGuides, accessed July 12, 2020, https://guides.rubyonrails.org/form_helpers.html
  2. “form_with,” API Dock, accessed July 12, 2020, https://apidock.com/rails/ActionView/Helpers/FormHelper/form_with

--

--

Scott Espinosa

Software Engineer based out of the San Francisco Bay Area. Flatiron School graduate with 8+ years background in healthcare.