Rails form_with

26 Sep 2023

This week I want to tell you about the form_with Rails helper. It was introduced in Rails 5.1, and the goal of this helper was to unify the form_for and the form_tag helpers. form_for requires a model instance or a scope and puts the attributes into a hash, so usually when you needed a form in the past where you didn’t want to put them scoped into a hash, you used form_tag.

Since the introduction of form_with, we can use the same helper for both use cases and specify a model, a scope, or just a URL. If you specify a model instance, it infers the URL and the scope. If it is a persisted one, it will generate an update form with the appropriate URL and HTTP method.

<%= form_with model: Blog.new do |form| %>
  <%= form.text_field :title %>
<% end %>
# will produce:

<form action="/blogs" method="post">
  ...
  <input type="text" name="blog[title]">
</form>


<%= form_with model: Blog.first do |form| %>
  <%= form.text_field :title %>
<% end %>
# will produce:
<form action="/blogs/1" accept-charset="UTF-8" method="post">
  <input type="hidden" name="_method" value="patch" autocomplete="off">
  ...
  <input type="text" value="testing" name="blog[title]" id="blog_title">
</form>

If you set a scope it will put all fields into a hash under the scope and if you only set a URL, the fields won’t be put into a hash:

# Adding a scope prefixes the input field names:
<%= form_with scope: :blog, url: blogs_path do |form| %>
  <%= form.text_field :title %>
<% end %>

# will produce:
<form action="/blogs" method="post">
  <input type="text" name="blog[title]">
</form>


<%= form_with url: blogs_path do |form| %>
  <%= form.text_field :title %>
<% end %>

# will produce:
<form action="/blogs" method="post">
  <input type="text" name="title">
</form>

You can also mix the above options and use a model with a custom scope and/or URL.

A few important things to remember about form_with:

  • if you call form_with without a block, it generates just an opening form tag
  • it has a local option, which is false by default, meaning it generates form tags submitted as a remote form with Rails UJS. This local option doesn’t control turbo though in Rails 7, if you want to disable that, you need to set the data-turbo attribute to false.

That’s it for the week. Until next time!

Hire me for a penetration test

Let's find the security holes before the bad guys do.

Did you enjoy reading this? Sign up to the Rails Tricks newsletter for more content like this!

Or follow me on Twitter

Related posts