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
- Rewrite the component as a module (module PrefixComponent; def prefix(wrapper_options); ...; end; end) and pass the module constant to include_component
- If the component was produced by Class.new/Struct.new, extract its instance methods into a plain module first
- 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
- Author components as modules only, never classes or Structs
- require component files at the top of the initializer — initializers run before Zeitwerk autoloading
- Add a shared spec that calls include_component and renders a form using each registered component
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
- %{name} method now accepts a `wrapper_options` argument. The
- Couldn't find wrapper with name #{name}
- Association cannot be used in forms not associated with an o
- :has_one associations are not supported by f.association
- input should be implemented by classes inheriting from Colle
AI-assisted analysis of heartcombo/simple_form@18f38aad0b (2026-08-21).
Data as JSON: /api/errors/3e2403ba0dd89c03.
Report an issue: GitHub.