Adding form errors to Superform fields

Display validation errors with any CSS framework

One of Superform’s strengths is giving developers complete control over how form errors are displayed. Rather than being locked into a specific CSS framework’s conventions, you can customize error styling to work with DaisyUI, Tailwind, Bootstrap, or any other framework you prefer.

Custom Field class with error helpers

The key is extending Superform::Rails::Form::Field with your own error helpers. Here’s an example that works with DaisyUI:

# ./app/components/form.rb
class Components::Form < Superform::Rails::Form
  class Field < Superform::Rails::Form::Field
    # A single sentence for inclusion below an input.
    def error_message
      "#{key.to_s.titleize} #{errors.to_sentence}" if errors.any?
    end

    def input(*, **attributes)
      super(*, **mix(
        {
          class: [
            "input input-outline w-full",
            ("input-error" if field.invalid?)
          ]
        },
        attributes)
      )
    end

    def textarea(*, **attributes)
      super(*, **mix(
        {
          class: [
            "textarea textarea-outline w-full",
            ("input-error" if field.invalid?)
          ]
        },
        attributes)
      )
    end
  end
end

How it works

The Field class provides two key features for error handling:

1. The error_message method

This generates a human-readable error message by combining the field name with its validation errors:

def error_message
  "#{key.to_s.titleize} #{errors.to_sentence}" if errors.any?
end

For a :email field with the error “is invalid”, this produces “Email is invalid”.

2. Conditional error classes

The field.invalid? method checks if the field has validation errors. When combined with Ruby’s conditional expression, you can add error-specific CSS classes:

class: [
  "input input-outline w-full",
  ("input-error" if field.invalid?)
]

When the field is valid, the class array is ["input input-outline w-full", nil]. Superform’s mix method filters out nil values, so you get clean HTML output.

Using it in your form

Render the error message below your input using the error_message helper:

class Views::Users::Form < Components::Form
  def view_template
    Field(:email).input(type: :email)
    p(class: "text-error text-sm mt-1") { Field(:email).error_message }

    Field(:password).input(type: :password)
    p(class: "text-error text-sm mt-1") { Field(:password).error_message }

    submit
  end
end

Adapting for other CSS frameworks

The pattern works identically for any CSS framework. Just swap out the class names:

Bootstrap

def input(*, **attributes)
  super(*, **mix(
    {
      class: [
        "form-control",
        ("is-invalid" if field.invalid?)
      ]
    },
    attributes)
  )
end

Plain Tailwind

def input(*, **attributes)
  super(*, **mix(
    {
      class: [
        "border rounded px-3 py-2 w-full",
        ("border-red-500" if field.invalid?)
      ]
    },
    attributes)
  )
end

The beauty of Superform is that you define these patterns once in your base Components::Form class, and they apply consistently across every form in your application.

Going deeper

The Phlex on Rails video course covers form validation patterns in detail, including how to build reusable field components with labels, hints, and error messages.

Do you want to learn Phlex 💪 and enjoy these code examples?

Support Beautiful Ruby by pre-ordering the Phlex on Rails video course.

Order the Phlex on Rails video course for $379