form_tag vs. form_for vs. form_with

Michelle Wong
Nov 5 · 4 min read

Rails provides form helpers that gives us a shortcuts to make our lives as developers a tad easier. In this article, I’m going to go over three options you’ll have in creating a form that allows your user to create or update the attributes of a specific model object.

form_tag

The first example I’m going to go over is form_tag. form_tag generates an HTML form for us and lets you specify options you want for your form.

In the example below, we use the rails method, url_for, to create a URL we want the form to submit to. This request for making a new post would have a URL of /posts and let Rails know the specific controller this URL is going to be for. Additionally, form_tag in this example uses the method option. Since HTML forms can only use GET and POST verbs, using the method option allows you to submit a form with, lets say, the delete HTTP verb as opposed to only using GET and POST.

Example of form_tag :

<%= form_tag url_for(action: ‘create’), method: “post” do %><%= label_tag ‘Title’ %><%= text_field_tag ‘title’, @post.title %><%= label_tag ‘Body’ %><%= text_area_tag ‘body’, @post.body %><%= label_tag ‘Author’ %><%= text_field_tag ‘author’, @post.author %><%= submit_tag “Create Post” %><% end %>

form_tag has helper methods for each tag we want to create:

label_tag -> <label></label>text_field_tag -> <input type=‘text’text_area_tag -> <textarea><textarea>submit_tag -> <input type=‘submit’

form_for

form_for method follows RESTful conventions on its own. It accepts the instance of the model as an argument where it makes assumptions for you (which is why it can be seen to be preferred over form_tag). form_for prefers the argument that you’re passing in to be an active record object. This will easily make a create or edit form.

Example of form_for :

<%= form_for @post do |f| %><%= f.label :category_name %><%= f.text_field :category_name %><%= f.text_field :content %><%= f.submit %><% end %>

Know when to use form_for vs. form_tag

We use form_for with a specific model and use form_tag when you don’t have a model for it (for custom URLs). Both form_for and form_tag produce HTML for a form. However, the syntax for both forms is different since we use form builder field helpers with form_for but not with form_tag. And then theres form_with where we always use form builder.

What’s with the form_with!?

The form_with view helper came out in 2017 with the idea to unify the interface of what form_for AND form_tag can do (so it is expected that form_with will be the one more commonly applied). When you pass it a model, it will act the same way as form_for would.

For example, if you have a Message model :

<%= form_with model: Post.new do |f| %><%= f.text_field :content %><% end %>

And if you don’t pass a model, form_with behaves like form_tag :

<%= form_with url: posts_path do |f| %><%= f.text_field :content %><% end %>

But, both would generate :

<form action=”/posts” method=”post” data-remote=”true”><input type=”text” name=”content”></form>

form_with submits are remote by default and attaches the data-remote=“true” to the form. You can change this by using local: true. This is the opposite from the other methods where the default is local and you have to specify remote: true to make it remote.

Example of form_with specifying local:true :

<%= form_with(model: Post.new, local:true) do |f| %><%= f.text_field :content %><% end %>

Another difference between form_with compared to form_for and form_tag is that form_for and form_tag generate automatic ids for the input fields. On the other hand, form_with does not. Ids and classes have to be specified. This isn’t necessarily considered to be a bad thing. It’s commonly thought that this function allows for developers to have freedom in how they get to design their forms.

<%= form_with model: @post do |f| %><%= f.label :author %><%= f.text_field :author, id: :post_author %><%= f.submit “Create this” %><% end %>

Lastly, another difference in form_with is that any id and class are not wrapped in the html key.

Example of form_with wrapping id & class in keys :

<%= form_with model: @post, id: “custom-id”, class: “custom-class” do |form| %>

as opposed to form_for where you have to specify the id and class:

<%= form_for @post, html: { id: “custom-id”, class: “custom-class” } do |form| %>

Hope this was helpful! Now go make those forms!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade