heartcombo/simple_form · error · TypeError

SimpleForm.include_component expects a module but got: #{com

Error message

SimpleForm.include_component expects a module but got: #{component.class}

What it means

SimpleForm.include_component includes a user-supplied module into SimpleForm::Inputs::Base so its methods can be referenced by name inside wrapper definitions (b.use :my_component). The guard 'Module === component' requires an actual Module; passing a Class, a String, nil, or any other object raises TypeError (lib/simple_form.rb:335). Classes are rejected on purpose: components are mixins, not input classes.

Source

Thrown at lib/simple_form.rb:335

  #      # Create a wrapper using the custom component.
  #      config.wrappers :input_group, tag: :div, error_class: :error do |b|
  #        b.use :label
  #        b.optional :prepend
  #        b.use :input
  #        b.use :append
  #      end
  #    end
  #
  #    # Using the custom component in the form.
  #    <%= simple_form_for @blog, wrapper: input_group do |f| %>
  #      <%= f.input :title, prepend: true %>
  #    <% end %>
  #
  def self.include_component(component)
    if Module === component
      SimpleForm::Inputs::Base.include(component)
    else
      raise TypeError, "SimpleForm.include_component expects a module but got: #{component.class}"
    end
  end
end

require 'simple_form/railtie' if defined?(Rails)

View on GitHub (pinned to 18f38aad0b)

Solutions

  1. Rewrite the component as a module (module PrefixComponent; def prefix(wrapper_options); ...; end; end) and pass the module constant to include_component
  2. If the component was produced by Class.new/Struct.new, extract its instance methods into a plain module first
  3. Make sure the module is loaded before the call: require it explicitly at the top of config/initializers/simple_form.rb or place it in lib and require it, since initializers run before Rails autoloading

Example fix

# before
class PrefixComponent
  def prefix
    # ...
  end
end
SimpleForm.include_component PrefixComponent  # TypeError: got Class

# after
module PrefixComponent
  def prefix(wrapper_options)
    template.content_tag(:span, options[:prefix], class: 'input-group-text')
  end
end
SimpleForm.include_component PrefixComponent
Defensive patterns

Strategy: type-guard

Validate before calling

raise TypeError, "component must be a Module, got #{component.class}" unless component.is_a?(Module)
SimpleForm.include_component(component)

Type guard

def component_module?(obj)
  obj.is_a?(Module) && !obj.nil?
end
# a Class instance is never is_a?(Module) in Ruby, so classes fail this check by design
component_module?(PrefixComponent) # => true

Try / catch

begin
  SimpleForm.include_component(component)
rescue TypeError => e
  Rails.logger.error("skipping component registration: #{e.message}")
end

Prevention

When it happens

Trigger: Calling SimpleForm.include_component(CustomComponent) where CustomComponent was declared with 'class' instead of 'module'; passing a constant built via Struct.new or Class.new; passing a symbol/string constant name; the argument being nil because the module file was not yet loaded when the initializer ran.

Common situations: Copy-pasting a custom component example but writing 'class CustomComponent'; defining the component as a Struct/Class.new metaprogrammed object; referencing an autoloaded (Zeitwerk) constant inside an initializer, which runs before autoload, so the value is nil or raises on the wrong object.

Related errors


AI-assisted analysis of heartcombo/simple_form@18f38aad0b (2026-08-21). Data as JSON: /api/errors/3e2403ba0dd89c03. Report an issue: GitHub.