From 6ef9440b88d6e528393ac8b2d57a046eaaddbcb3 Mon Sep 17 00:00:00 2001 From: Jared Armstrong Date: Tue, 14 Apr 2026 13:37:30 +1200 Subject: [PATCH 1/2] Fix stale content cache when slots are accessed before render_in render_in resets @__vc_content_evaluated but does not clear the cached @__vc_content. If content was evaluated before render_in (e.g. via a slot predicate calling __vc_get_slot), the stale nil is returned on every subsequent call and the render block is silently ignored. Clear @__vc_content in render_in so the cache is fully invalidated. Co-Authored-By: Claude Opus 4.6 --- lib/view_component/base.rb | 1 + .../slot_with_content_block_component.rb | 12 ++++++++++++ test/sandbox/test/slotable_test.rb | 17 +++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 test/sandbox/app/components/slot_with_content_block_component.rb diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index b251991d7..f82fb6d46 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -131,6 +131,7 @@ def render_in(view_context, &block) end @__vc_content_evaluated = false + remove_instance_variable(:@__vc_content) if defined?(@__vc_content) @__vc_render_in_block = block @view_context.instance_variable_set(:@virtual_path, virtual_path) diff --git a/test/sandbox/app/components/slot_with_content_block_component.rb b/test/sandbox/app/components/slot_with_content_block_component.rb new file mode 100644 index 000000000..dcd9e3c2f --- /dev/null +++ b/test/sandbox/app/components/slot_with_content_block_component.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class SlotWithContentBlockComponent < ViewComponent::Base + renders_one :header + + def call + out = +"" + out << content_tag(:div, header, class: "header") if header? + out << content_tag(:div, content, class: "body") if content? + out.html_safe + end +end diff --git a/test/sandbox/test/slotable_test.rb b/test/sandbox/test/slotable_test.rb index d13be3692..778ece663 100644 --- a/test/sandbox/test/slotable_test.rb +++ b/test/sandbox/test/slotable_test.rb @@ -822,6 +822,23 @@ def test_slot_name_methods_are_not_shared_accross_components assert_not_equal SlotsComponent.instance_method(:title).owner, SlotNameOverrideComponent::OtherComponent.instance_method(:title).owner end + def test_slot_predicate_before_render_does_not_poison_content_cache + component = SlotWithContentBlockComponent.new + component.with_header { "My Header" } + + # Accessing a slot predicate before render_in triggers content evaluation + # via __vc_get_slot. Without the fix, this caches a nil @__vc_content that + # persists through render_in, causing the content block to be silently ignored. + assert component.header? + + render_inline(component) do + "Body content from block" + end + + assert_selector(".header", text: "My Header") + assert_selector(".body", text: "Body content from block") + end + def test_allows_marking_slot_as_last render_inline(LastItemComponent.new) do |component| component.with_item("animal") From ebf0e151cc3f0fcae365b63f253183b0f55485c8 Mon Sep 17 00:00:00 2001 From: Joel Hawksley Date: Tue, 14 Apr 2026 13:28:09 -0600 Subject: [PATCH 2/2] changelog --- docs/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 592c186bf..349ed2d94 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,6 +10,10 @@ nav_order: 6 ## main +* Fix stale content cache when slots are accessed before `render_in`. + + *Jared Armstrong* + * Add rubocop-view_component to resources. *Andy Waite*