ViewComponent/view_component · error · ViewComponent::Serializable::UnserializableError
Cannot serialize render_in with a block
Error message
Cannot serialize render_in with a block
What it means
The Proxy returned by render_later implements the Rails renderable interface via render_in, but the whole point of the proxy is that it can be serialized — and blocks can't be. If render_in is called with a block (e.g., render(proxy) { ... } in a template, or broadcast-style calls that capture a block), it raises ViewComponent::Serializable::UnserializableError ('Cannot serialize render_in with a block') before building the component. Content data must be attached beforehand via with_* slot calls that take arguments only.
Source
Thrown at lib/view_component/serializable/proxy.rb:44
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)
capture_slot_call(method_name, args, &block)
else
super
end
end
ruby2_keywords :method_missing
def respond_to_missing?(method_name, include_private = false)
slot_method?(method_name) || super
end
# Returns a hash that can be rebuilt into a Proxy instance using +deserialize+View on GitHub (pinned to 9f22c36fa7)
Solutions
- Remove the block from the render call: <%= render(MyComponent.render_later("x")) %>.
- Supply the would-be block content as slot arguments instead: render(MyComponent.render_later("x").with_body("<p>content</p>")) using a slot the component renders.
- If the block genuinely needs the render-time view context, render the concrete component (MyComponent.new(...)) rather than the proxy.
Example fix
# before (ERB)
<%= render(MyComponent.render_later("title")) do %>
<p>Block content</p>
<% end %>
# after (ERB)
<%= render(MyComponent.render_later("title").with_body("<p>Block content</p>")) %> Defensive patterns
Strategy: validation
Validate before calling
def render_deferred(view_context, proxy, &block) raise ArgumentError, "proxies cannot render with a block" if block view_context.render(proxy) end
Prevention
- Render proxies block-free: <%= render(proxy) %> with all data attached via with_* calls beforehand.
- Standardize a helper (e.g., render_deferred) that forbids blocks so misuse fails at the call site.
When it happens
Trigger: <%= render(MyComponent.render_later("x")) do %>Block<% end %> in an ERB template; passing a content block when broadcasting a deferred component with Turbo Streams; generic code that always yields a block to any renderable object it renders.
Common situations: Swapping a live component for a deferred proxy in an existing template that still has a block; helper methods that render arbitrary renderables with an optional block; migrating broadcast calls to the proxy API.
Related errors
- Cannot serialize render_later with a block
- Cannot deserialize unknown component: #{hash["component_clas
- Cannot serialize anonymous component class #{component_class
- Cannot serialize slot call '#{method_name}' with a block
- Inline templates can only be defined once per-component.
AI-assisted analysis of ViewComponent/view_component@9f22c36fa7 (2026-08-23).
Data as JSON: /api/errors/3bf1c4e7c2b583eb.
Report an issue: GitHub.