{"record":{"id":"71db0fccc8720fbe","repo":"ViewComponent/view_component","slug":"cannot-serialize-render-later-with-a-block","errorCode":null,"errorMessage":"Cannot serialize render_later with a block","messagePattern":"Cannot serialize render_later with a block","errorType":"exception","errorClass":"ViewComponent::Serializable::UnserializableError","httpStatus":null,"severity":"error","filePath":"lib/view_component/serializable.rb","lineNumber":20,"sourceCode":"\nrequire \"active_support/concern\"\nrequire \"view_component/serializable/proxy\"\n\nmodule ViewComponent\n  module Serializable\n    extend ActiveSupport::Concern\n\n    class UnserializableError < ArgumentError; end\n\n    class_methods do\n      # Returns a Proxy that captures this class and its initialization arguments,\n      # deferring instantiation until render time. The proxy is renderable and\n      # serializable for ActiveJob (e.g. Turbo Streams). Slot calls made on the\n      # proxy are captured and replayed at render time. Blocks are not supported.\n      #\n      #   MyComponent.render_later(\"title\", size: :large).with_item(label: \"One\")\n      def render_later(*args, &block)\n        raise UnserializableError, \"Cannot serialize render_later with a block\" if block\n\n        ViewComponent::Serializable::Proxy.new(self, *args)\n      end\n      ruby2_keywords :render_later\n    end\n  end\nend\n","sourceCodeStart":2,"sourceCodeEnd":28,"githubUrl":"https://github.com/ViewComponent/view_component/blob/9f22c36fa7d7b44098a8ed97472e3b4674ae420f/lib/view_component/serializable.rb#L2-L28","documentation":"Component.render_later (ViewComponent::Serializable) returns a Proxy that defers instantiation until render time and can be handed to ActiveJob / Turbo Streams, which serialize it to a plain hash. Ruby blocks cannot be serialized, so render_later raises ViewComponent::Serializable::UnserializableError (an ArgumentError subclass) immediately if a block is passed. Slot data must instead be supplied through serializable slot-call arguments (with_* methods without blocks).","triggerScenarios":"MyComponent.render_later { \"content\" }; appending a content block like render_later(args) { |c| ... }; migrating synchronous render(MyComponent.new) { ... } calls to the deferred API without removing the block.","commonSituations":"Moving inline Turbo Stream broadcasts into background jobs and carrying over block-style content; templates that pass a block when rendering; habit from render/render_in which both accept blocks.","solutions":["Remove the block and pass content via serializable slot calls: MyComponent.render_later(\"title\").with_item(label: \"One\").","If content must be computed now, capture it into a string/variable first and pass it as a slot or initializer argument (plain data serializes fine).","If a block is essential (content needs the view context at render time), render synchronously with MyComponent.new(...).render_in(view_context) instead of render_later."],"exampleFix":"# before\nproxy = MyComponent.render_later(\"title\", size: :large) do\n  \"<p>Block content</p>\"\nend # UnserializableError\n\n# after\nproxy = MyComponent.render_later(\"title\", size: :large).with_item(label: \"One\")","handlingStrategy":"validation","validationCode":"def deferred(component_class, *args, &block)\n  raise ArgumentError, \"blocks are not supported by render_later\" if block\n  component_class.render_later(*args)\nend","typeGuard":null,"tryCatchPattern":"begin\n  proxy = MyComponent.render_later(\"title\")\nrescue ViewComponent::Serializable::UnserializableError\n  # fall back to synchronous render when a block/content cannot be deferred\n  render(MyComponent.new(\"title\"))\nend","preventionTips":["Treat render_later as data-only: pass strings/numbers/hashes, never blocks or Procs.","Wrap deferred calls in a small helper that rejects blocks loudly at the call site.","Convert block content into a slot argument before enqueueing."],"tags":["view-component","ruby-on-rails","serialization","activejob","turbo-streams"],"backgroundTag":"block-not-serializable","analyzedSha":"9f22c36fa7d7b44098a8ed97472e3b4674ae420f","analyzedAt":"2026-08-23T10:20:47.008Z","schemaVersion":2},"datasetVersion":"2026-08-23T13:39:53.451Z"}