How to Use Form_with and Why You Should Start Now

Claire DeBoer
3 min readDec 22, 2020

--

When I first started learning about forms, I found it really confusing to have two different versions depending on the situation. Form_tag is used when you don’t have a model while form_for is used with a model. It’s a lot of syntax to remember.

Apparently David Heinemeier Hansson felt the same way. Form_tag and form_for are soft deprecated. They are being replaced with form_with for Rails 5.1 and above. It’s time to get on board with form_with. Here’s how to use the latest iteration of forms and also why it’s superior to the old ways of writing forms.

Only one syntax:

The beauty of form_with is that it can be used either with or without a model. We can tell form_with if we are using a model or a url for building the form. See the two examples below.

Form_with without a model example:

<%= form_with url: products_path do |form| %>
<%= form.label :price %>
<%= form.text_field :price %>
<%= form.submit %>
<% end %>

Form_with with a model example:

<%= form_with model: @product do |form| %>
<%= form.label :price %>
<%= form.text_field :price %>
<%= form.submit %>
<% end %>

This is what the HTML looks like:

<form action="/products" accept-charset="UTF-8" method="post">
<input name="utf8" type="hidden" value="&#x2713;" />
<input type="hidden" name="authenticity_token" value="..." />
<label>Price</label>
<input type="text" name="price" />
<input type="submit" name="commit" value="Save " data-disable- with="Save " />
</form>

UTF-8: A character in UTF-8 can be from 1 to 4 bytes long. UTF-8 can represent any character in the Unicode standard. It forces the browser to submit the form in UTF-8 encoding mode.

<input type=”hidden”>: Defines a hidden input field. A hidden field lets developers include data that cannot be seen or modified by users when a form is submitted.

Authenticity token: When the user views a form, Rails creates a random authenticity_token, stores this token in the session, and places it in a hidden field in the form. When the user submits the form, Rails looks for the authenticity_token, compares it to the one stored in the session, and only processes the request if they match. This prevents Cross Site Request forgery. CSRF protection is turned on with the protect_from_forgery method. (via Stack Overflow)

Classes (and ids) are not automatic:

If you want to add an HTML class to your form, you can add it in Ruby and it will show up in the HTML. This is easier than having to go into the HTML to add classes or dealing with the automatically generated classes that come with form_tag and form_for. Make note though that in Rails 5.1 you have to manually set both ids and classes. As of Rails 5.2, ids will be automatically generated so they function properly with labels and Capybara tests.

<%= form_with model: @product do |form| %>
<%= form.label :price %>
<%= form.text_field :price, id: :price, class: :price %>
<%= form.submit %>
<% end %>

Here’s the resulting HTML:

<form action="/products" ...>

<input id=”price” class=”price” type="text" name=”product[price]” />
</form>

Form fields don’t have to match the attributes assigned in the schema:

You can assign custom fields using the same syntax as you would with assigned attributes. Custom_message in the example below is not in the schema for Product, instead it comes in from the params and can be used later in the Product controller.

Labels for all three types of forms can be whatever you want displayed and don’t have to match attributes.

Here’s an example:

<%= form_with model: @product do |form| %>
<%= form.label :price %>
<%= form.text_field :price %>
<%= form.label ‘Please add custom requests for product here’ %>
<%= form.text_field :custom_message %>
<%= form.submit "create" %>
<% end %>

Hopefully this explanation helps you start implementing form_with in your code and enjoying its benefits. Form_with is the future!

API Documentation

DHH’s original proposal

--

--

Responses (1)