ViewComponent/view_component · error · ViewComponent::AlreadyDefinedPolymorphicSlotSetterError
A method called 'SETTER_METHOD_NAME' already exists and woul
Error message
A method called 'SETTER_METHOD_NAME' already exists and would be overwritten by the 'SETTER_NAME' polymorphic slot setter. Please choose a different setter name.
What it means
ViewComponent raises this error while registering a polymorphic slot. When renders_one or renders_many receives a types: hash, __vc_register_polymorphic_slot derives a setter method named with_<poly_slot_name> for each type (default <slot>_<type> for singular slots, <singularized_slot>_<type> for collections, overridable with as:). Before calling define_method it checks method_defined?(setter_method_name) at lib/view_component/slotable.rb:262 and raises instead of silently overwriting an existing method.
Source
Thrown at lib/view_component/slotable.rb:263
poly_callable = poly_attributes_or_callable
poly_slot_name = nil
end
poly_slot_name ||=
if collection
"#{ActiveSupport::Inflector.singularize(slot_name)}_#{poly_type}"
else
"#{slot_name}_#{poly_type}"
end
memo[poly_type] = __vc_define_slot(
poly_slot_name, collection: collection, callable: poly_callable
)
setter_method_name = :"with_#{poly_slot_name}"
if method_defined?(setter_method_name)
raise AlreadyDefinedPolymorphicSlotSetterError.new(setter_method_name, poly_slot_name)
end
define_method(setter_method_name) do |*args, **kwargs, &block|
__vc_set_polymorphic_slot(slot_name, poly_type, *args, **kwargs, &block)
end
define_method :"with_#{poly_slot_name}_content" do |content|
send(setter_method_name) { content.to_s }
self
end
end
registered_slots[slot_name] = {
collection: collection,
renderable_hash: renderable_hash
}
endView on GitHub (pinned to 9f22c36fa7)
Solutions
- Rename the colliding polymorphic type with as: so its setter becomes unique (for example as: :heading)
- Rename or remove the other slot or instance method that already owns the with_<name> setter
- Check ancestors with SomeComponent.method_defined?(:with_row_header) to locate which class defines the colliding method
- Keep polymorphic type keys and as: aliases unique per component
Example fix
# before
class TableComponent < ViewComponent::Base
renders_one :row_header, RowHeaderComponent # defines with_row_header
renders_many :rows, types: {
header: { renders: RowHeaderComponent, as: :row_header } # collides with with_row_header
}
end
# after
class TableComponent < ViewComponent::Base
renders_one :row_header, RowHeaderComponent
renders_many :rows, types: {
header: { renders: RowHeaderComponent, as: :row_heading }
}
end Defensive patterns
Strategy: validation
Validate before calling
# mirror the framework check before declaring a polymorphic slot
def polymorphic_setters_free?(klass, slot_name, types, collection:)
types.all? do |type, opts|
base = collection ? slot_name.to_s.singularize : slot_name.to_s
name = (opts.is_a?(Hash) ? opts[:as] : nil) || "#{base}_#{type}"
!klass.method_defined?("with_#{name}")
end
end
polymorphic_setters_free?(MyComponent, :rows, { header: { renders: RowHeaderComponent } }, collection: true) # => true when free Try / catch
# only for config-driven registration; static declarations should fail at boot begin renders_many :rows, types: configured_types rescue ViewComponent::AlreadyDefinedPolymorphicSlotSetterError configured_types = sanitize_type_aliases(configured_types) retry end
Prevention
- Compute every derived setter name (with_ plus base underscore type) before declaring a polymorphic slot
- Give polymorphic types distinctive as: names instead of generic ones like header or item
- Before adding slots in a subclass, list inherited names with SomeParent.registered_slots.keys
- Keep polymorphic types in one place so entries cannot drift into collisions across edits
When it happens
Trigger: Declaring a polymorphic slot whose derived setter already exists. Examples: renders_one :row_header defines with_row_header, then renders_many :rows, types: { header: { renders: RowHeaderComponent, as: :row_header } } derives with_row_header again; two entries in one types hash deriving the same name; a hand-written def with_item_extra method; or an ancestor class already defining the setter, since method_defined? includes inherited methods.
Common situations: Adding a polymorphic slot to a component that already has regular slots with overlapping names; refactoring a plain renders_one slot into a polymorphic renders_many without renaming; choosing short as: aliases such as :header that collide with existing with_header setters; deep inheritance where a parent class defines the colliding method.
Related errors
- COMPONENT declares a slot named SLOT_NAME, which is a reserv
- COMPONENT declares a slot named content, which is a reserved
- COMPONENT declares the SLOT_NAME slot multiple times. To fi
- COMPONENT declares a slot named SLOT_NAME, which ends with a
- Slot cannot start with 'call_'. Please rename #{slot_name}
AI-assisted analysis of ViewComponent/view_component@9f22c36fa7 (2026-08-23).
Data as JSON: /api/errors/4e5b0183bcffe406.
Report an issue: GitHub.