Better Rails Forms with Superform

Illustration of a web form, embodied as a superhero creature, punching the sun.
Image by Annie Ruygt

Rails form helpers have been around for so long that they seem unquestionable, but did you know there’s a way to build forms in Rails that are easier to customize and permit their own strong parameters?

Superform is a new form helper library built from scratch on top of Phlex that makes building forms in Rails less tedious. Here’s what a Superform looks like:

# ./app/views/posts/form.rb
class Posts::Form < ApplicationForm
  def template(&)
    labeled field(:title).input
    labeled field(:body).textarea
  end

  # This would actually be in the ApplicationForm
  # superclass. It's added here for clarity.
  def labeled(component)
    div "p-4" do
      render component.field.label
      render component
    end
  end
end

It can then be rendered in Erb, HAML, or any other templating library. Like any view component: create an instance, pass it the @post, and you’ll see a form.

<h1>New post</h1>
<%= render Posts::Form.new @post %>

But the real superpower comes from creating an instance of the form in the controller so it can permit its own parameters.

class PostsController < ApplicationController
  include Superform::Rails::StrongParameters

  def create
    @form = Posts::Form.new(Post.new)
    @post = assign params.require(:post), to: @form

    if @post.save
      # Success path
    else
      # Error path
    end
  end
end

Never again will you wonder, “why is my form not updating?” in your Rails development environment only to realize you forgot to add a strong parameter to Rails. 🤦‍♂️

How does Superform permit parameters?

Unlike Rails form helpers, Superform keeps tracks of the field names when the field() method is called in the view where you build the form. For example, in this form below the :title and :body attributes are passed into the field method, which adds them to an internal list of permitted parameters.

# ./app/views/posts/form.rb
class Posts::Form < ApplicationForm
  def template(&)
    labeled field(:title).input
    labeled field(:body).textarea
  end
end

Superform doesn’t actually use Rails strong parameters though—instead the assign method recursively goes through each field defined in the Superform and assigns the corresponding parameter to that field. If the field is not defined, its not assigned.

assign params.require(:post), to: @form

It’s not really magic, it’s just a form builder that’s both aware of its own structure and visual appearance. Contrast with Rails built-in form builders, which is only capable of describing visual its appearance without knowledge of the fields it’s permitting.

Install Superform in your Rails app

If you’re read to get your hands dirty with Superform, add it to your Rails app.

$ bundle add superform
$ rails g superform:install

This installs Phlex and Superform in your Rails app. You’ll also see the ./app/views/application_form.rb file in your Rails project that will look familiar to the forms above. Read the Superform docs to see how the form helpers work and have a look at the Phlex website to get a feel for the basics of how you’d build your own HTML components using Ruby.

Customize the look and feel of forms

Since Superform is built on top of Phlex, inputs can be extended to emit the HTML markup your application needs from the ApplicationForm class. Here’s an example where we create a MyInputComponent class that includes inline error messages.

# ./app/views/forms/application_form.rb
class ApplicationForm < Superform::Rails::Form
  class MyInputComponent < ApplicationComponent
    def template(&)
      div class: "form-field" do
        input(**attributes)
        if field.errors?
          p(class: "form-field-error") { field.errors.to_sentence }
        end
      end
    end
  end

  class Field < Field
    def input(**attributes)
      MyInputComponent.new(self, attributes: attributes)
    end
  end
end

Additionally, methods can be added to the ApplicationForm class that makes common form tasks a bit easier.

class ApplicationForm
   def labeled(component)
    div class: "p-4" do
      render component.field.label
      render component
    end
  end

  def submit(text)
    button(type: :submit) { text }
  end
end

These methods can be called from form sub-classes and HTML will be emitted.

Wrap up

Superform is a great way to create highly re-usable and customizable forms that permit their own parameters in Rails.

Superform permits its own parameters - Makes building forms much faster since you don’t have to worry about adding the parameter to the controller while building a form.

Forms can be customized with Phlex components - Superform is built from the ground-up with Phlex components. This makes it easier to mix regular HTML into the helper.

Superform is still a work in progress. It hasn’t reached 1.0 yet and features like select > options are still being developed, but already its being used for sophisticated form use cases, like batch selection UI in a demo app.