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
        }
      end

View on GitHub (pinned to 9f22c36fa7)

Solutions

  1. Rename the colliding polymorphic type with as: so its setter becomes unique (for example as: :heading)
  2. Rename or remove the other slot or instance method that already owns the with_<name> setter
  3. Check ancestors with SomeComponent.method_defined?(:with_row_header) to locate which class defines the colliding method
  4. 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

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


AI-assisted analysis of ViewComponent/view_component@9f22c36fa7 (2026-08-23). Data as JSON: /api/errors/4e5b0183bcffe406. Report an issue: GitHub.