diff --git a/app/models/turbo/streams/tag_builder.rb b/app/models/turbo/streams/tag_builder.rb
index 99a2aa47..a788973f 100644
--- a/app/models/turbo/streams/tag_builder.rb
+++ b/app/models/turbo/streams/tag_builder.rb
@@ -245,6 +245,8 @@ def action_all(name, targets, content = nil, allow_inferred_rendering: true, **r
private
def render_template(target, content = nil, allow_inferred_rendering: true, **rendering, &block)
case
+ when target.respond_to?(:render_in)
+ target.render_in(@view_context, &block)
when content.respond_to?(:render_in)
content.render_in(@view_context, &block)
when content
diff --git a/test/streams/streams_helper_test.rb b/test/streams/streams_helper_test.rb
index e8511dfb..a7649a37 100644
--- a/test/streams/streams_helper_test.rb
+++ b/test/streams/streams_helper_test.rb
@@ -43,4 +43,31 @@ class Turbo::StreamsHelperTest < ActionView::TestCase
HTML
end
+
+ test "supports valid :partial option objects" do
+ message = Message.new(id: 1, content: "Hello, world")
+
+ assert_dom_equal <<~HTML.strip, turbo_stream.update(message)
+ Hello, world
+ HTML
+ end
+
+ test "supports valid :renderable option objects" do
+ component_class = Class.new do
+ def self.model_name
+ ActiveModel::Name.new(self, nil, "Component")
+ end
+ delegate :model_name, to: :class
+
+ def initialize(id:, content:) = (@id, @content = id, content)
+ def render_in(...) = @content
+ def to_key = [@id]
+ end
+
+ component = component_class.new(id: 1, content: "Hello, world")
+
+ assert_dom_equal <<~HTML.strip, turbo_stream.update(component)
+ Hello, world
+ HTML
+ end
end