"form_for" vs. ".form" vs "f = form_for"

In various code samples, I see different ways to create a form in a .heex template.

What is the difference between using the following:

<%= form_for @changeset, "/to_path", ...other_opts, fn f -> %>
...
<% end %>
<%= f = form_for @changeset, "to_path, ...other_opts %>
...
<% end %>
<.form let={f} for={@changeset} action={"to_path"} ...other_opts>
...
</.form>

Is there a best practice, or is it just up to the preferences of the developer?

1 Like

I found some answers in the Phoenix.HTML docs:

  1. <%= f = form_for @changeset ... %> is now deprecated. Use <%= form_for @changeset ... %> instead.

  2. <.form> is a “function component”, in the context of Phoenix LiveViews.

This leads me to a follow-up question: If I don’t use LiveViews, should I still use <.form>?

1 Like

You’re right about the difference. According to the LiveView docs, <.form> is just a wrapper around form_for and should generally be preferred if you’re using HEEx templates (including on deadviews). Just remember to give the action attribute!

See Phoenix.LiveView.Helpers documentation under “Outside LiveView”

7 Likes

Doh! Link is dead

Here you go!
Example: Form Outside LiveView (regular HTTP requests)

1 Like

And for the ones of us running Phoenix.LiveView pre-0.18 (e.g., 0.17.14) there is also the “Outside LiveView” example under the Phoenix.LiveView.Helpers.form/1 documentation.

I know this thread is old, but I was struggling with the same question, so I’ll post the answer in case anyone else comes across this thread like I did.

tl;dr:

  • form_for should no longer be used and will eventually be removed from Phoenix.
  • In LiveView, use <.form for={@form}> where @form is a Phoenix.HTML.Form struct (generally created using to_form). Don’t use :let.
  • Outside of LiveView, you can also pass a changeset or map to for, and it’ll be converted to a Form which can be captured with :let. (This approach is also technically possible in a LiveView, but it’s discouraged because it makes change tracking less efficient.)

The second and third points are made clear in the docs for form/1. The first point was confirmed to me by none other than Chris McCord; I wrote about it here:

4 Likes

Correct. .form is the way to go, form_for will forever exist for backwards compatibility, f = form_for is deprecated and should be rewritten.

4 Likes