Collections

Since 2.1.0

Like Rails partials, it’s possible to render a collection with ViewComponents, using with_collection:

<%= render(ProductComponent.with_collection(@products)) %>
class ProductComponent < ViewComponent::Base
  def initialize(product:)
    @product = product
  end
end

By default, the component name is used to define the parameter passed into the component from the collection.

with_collection_parameter

Use with_collection_parameter to change the name of the collection parameter:

class ProductComponent < ViewComponent::Base
  with_collection_parameter :item

  def initialize(item:)
    @item = item
  end
end

Additional arguments

Additional arguments besides the collection are passed to each component instance:

<%= render(ProductComponent.with_collection(@products, notice: "hi")) %>
class ProductComponent < ViewComponent::Base
  with_collection_parameter :item

  erb_template <<-ERB
    <li>
      <h2><%= @item.name %></h2>
      <span><%= @notice %></span>
    </li>
  ERB

  def initialize(item:, notice:)
    @item = item
    @notice = notice
  end
end

Collection counter

Since 2.5.0

ViewComponent defines a counter variable matching the parameter name above, followed by _counter. To access the variable, add it to initialize as an argument:

class ProductComponent < ViewComponent::Base
  erb_template <<-ERB
    <li>
      <%= @counter %> <%= @product.name %>
    </li>
  ERB

  def initialize(product:, product_counter:)
    @product = product
    @counter = product_counter
  end
end

Collection iteration context

Since 2.33.0

ViewComponent defines an iteration variable matching the parameter name above, followed by _iteration. This gives contextual information about the iteration to components within the collection (#size, #index, #first?, and #last?).

To access the variable, add it to initialize as an argument:

class ProductComponent < ViewComponent::Base
  erb_template <<-ERB
    <li class="<%= "featured" if @iteration.first? %>">
      <%= @product.name %>
    </li>
  ERB

  def initialize(product:, product_iteration:)
    @product = product
    @iteration = product_iteration
  end
end

Spacer components

Since 3.20.0

Set :spacer_component as an instantiated component to render between items:

<%= render(ProductComponent.with_collection(@products, spacer_component: SpacerComponent.new)) %>

Which will render the SpacerComponent component between ProductComponents.