ViewComponent/view_component · error · ViewComponent::Serializable::UnserializableError

Cannot serialize anonymous component class #{component_class

Error message

Cannot serialize anonymous component class #{component_class.inspect}

What it means

Proxy#initialize requires a named component class because serialization stores component_class.name and deserialization resolves it back via safe_constantize. An anonymous class (Class.new(ViewComponent::Base) never assigned to a constant, so name is nil) has no stable name, so Proxy.new — reached via Component.render_later — raises ViewComponent::Serializable::UnserializableError ('Cannot serialize anonymous component class'). The check is component_class.name.nil?.

Source

Thrown at lib/view_component/serializable/proxy.rb:32

        raise ArgumentError, "Cannot deserialize unknown component: #{hash["component_class"]}" unless klass

        args = ActiveJob::Arguments.deserialize(hash["initialize_args"] || [])
        proxy = new(klass, *args)

        Array(hash["slot_calls"]).each do |call|
          method_name = call["method"].to_sym
          slot_args = ActiveJob::Arguments.deserialize(call["args"])
          proxy.public_send(method_name, *slot_args)
        end

        proxy
      end

      attr_reader :component_class, :initialize_args, :slot_calls

      def initialize(component_class, *args)
        if component_class.name.nil?
          raise UnserializableError, "Cannot serialize anonymous component class #{component_class.inspect}"
        end

        @component_class = component_class
        @initialize_args = args
        @slot_calls = []
      end
      ruby2_keywords :initialize

      # Implements the Rails renderable interface
      def render_in(view_context, &block)
        if block
          raise UnserializableError, "Cannot serialize render_in with a block"
        end
        build_component.render_in(view_context)
      end

      def method_missing(method_name, *args, &block)
        if slot_method?(method_name)

View on GitHub (pinned to 9f22c36fa7)

Solutions

  1. Assign the class to a constant so .name returns a resolvable string: define it with `class DynamicCardComponent < ViewComponent::Base` in a file, or use const_set on the intended namespace.
  2. If generating classes at runtime, register each one under a stable, unique constant name before calling render_later.
  3. If the component must stay anonymous, render it synchronously (build the instance and call render_in) instead of using render_later.

Example fix

# before
card = Class.new(ViewComponent::Base) do
  erb_template "<div><%= content %></div>"
end
proxy = card.render_later # UnserializableError: anonymous class

# after
class DynamicCardComponent < ViewComponent::Base
  erb_template "<div><%= content %></div>"
end
proxy = DynamicCardComponent.render_later
Defensive patterns

Strategy: validation

Validate before calling

component_class = Class.new(ViewComponent::Base) { erb_template "<div>" }
raise ArgumentError, "component must be named to defer-render" if component_class.name.nil?

component_class.render_later

Prevention

When it happens

Trigger: Class.new(ViewComponent::Base) { erb_template ... }.render_later; dynamically built components kept in local variables and never assigned to a constant; test factories creating component classes programmatically and then passing them to render_later.

Common situations: DSL/builder patterns that generate component classes at runtime; specs that define throwaway components with Class.new; helper methods that return newly defined classes.

Related errors


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