You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Core] generated_stylesheet_hrefs_json runs for OSS apps on every auto-loaded component render, but only the Pro client reads data-generated-stylesheet-hrefs #4341
generated_stylesheet_hrefs_json (added in #4047) runs on everyreact_component render that uses auto_load_bundle, for OSS and Pro alike — but the attribute it emits, data-generated-stylesheet-hrefs, is consumed only by the Pro client package (packages/react-on-rails-pro/src/ClientSideRenderer.ts, FOUC reveal gating). For OSS users this is pure hot-path waste: in development (Shakapacker cache_manifest: false) each call re-reads and JSON-parses manifest.json per component per request; in production it computes hrefs and emits a JSON attribute that nothing ever reads.
PR #4047 ("Fix Pro FOUC reveal gating"), merged 2026-06-15 (commit af6525d8b). Confirmed via git log --oneline -S "generated_stylesheet_hrefs_json" 43ed0e138..origin/main — single hit.
Mechanism
Every react_component / react_component_hash call goes through internal_react_component → generate_component_script(render_options) (ProHelper is unconditionally included into ReactOnRails::Helper, so OSS apps run it too).
generate_component_script computes the attribute for every auto-loaded component:
preload_sources_for_stylesheet_pack → preload_sources_for_pack → current_shakapacker_instance.manifest.lookup_pack_with_chunks(pack_name, type: :stylesheet). With Shakapacker's cache_manifest: false (the development default), each lookup_pack_with_chunks call refreshes the manifest — a File.read + JSON.parse of manifest.json — so this happens once per auto-loaded component per request, on top of the lookups the framework already does for pack tags.
The only reader of the resulting data-generated-stylesheet-hrefs attribute is ClientSideRenderer.ts in packages/react-on-rails-pro (GENERATED_STYLESHEET_HREFS_ATTRIBUTE, used by generatedStylesheetMatchesComponent for FOUC reveal gating). The OSS client package never reads it (verified by grep across packages/).
Failure scenario
Not a correctness failure — a per-render performance/waste regression:
OSS app in development with several auto-loaded components per page: each page render performs N extra full manifest.json read+parse cycles (manifest files in real apps are commonly hundreds of KB), inflating the dev feedback loop.
OSS app in production: extra manifest hash lookups, href de-duplication, to_json, and dead HTML bytes (a JSON array of stylesheet URLs) on every component spec tag — payload that no client code consumes.
Impact
P2 performance/cleanliness: measurable dev-mode overhead for OSS apps with auto_load_bundle, plus dead-weight HTML in production responses. No behavior break.
Pro apps pay the same dev-mode re-read cost per component even though a single per-request (or per pack name) memoization would suffice.
Suggested fix
Two independent, compatible changes:
Early-return for OSS, since only the Pro client consumes the attribute:
defgenerated_stylesheet_hrefs_json(render_options)returnunlessReactOnRails::Utils.react_on_rails_pro?returnunlessrender_options.auto_load_bundle
...
end
Memoize the href computation per pack name per request (mirroring other per-request caches in the helper):
(Helper instances are per-request in Rails, so an ivar cache is request-scoped.)
Test plan
Spec (OSS mode, react_on_rails_pro? stubbed false): render an auto-loaded component; assert the spec tag has nodata-generated-stylesheet-hrefs attribute and preload_sources_for_stylesheet_pack is not called.
Spec (Pro mode): attribute still emitted with correct hrefs — existing Fix Pro FOUC reveal gating #4047 coverage in react_on_rails/spec/dummy/spec/helpers/react_on_rails_helper_spec.rb should keep passing.
Spec: two components sharing a generated pack name in one render only trigger one manifest lookup (memoization).
Pro client test packages/react-on-rails-pro/tests/ClientSideRenderer.test.ts unaffected.
Run: cd react_on_rails && bundle exec rspec spec/dummy/spec/helpers/react_on_rails_helper_spec.rb.
Notes
Verified against origin/main 5264192cc by reading pro_helper.rb, helper.rb:526-565, and grepping all of packages/ for the attribute — consumers are only the Pro ClientSideRenderer.ts and its test. Confidence: high. The dev-mode re-read claim rests on Shakapacker's documented cache_manifest: false behavior (manifest refreshed on each lookup), not re-measured here.
A local fix session for this may already be queued (a task chip exists in the reporting workspace); this issue is the tracking artifact either way.
Related finding (same review): "[Core] add_rsc_stream_observability_to_render_options writes through to the options hash held by RenderOptions" (same helper hot path).
Found in a post-2026-06-12 code review of merged changes (review base 43ed0e1). Filed by Claude Code on behalf of Justin Gordon.
Summary
generated_stylesheet_hrefs_json(added in #4047) runs on everyreact_componentrender that usesauto_load_bundle, for OSS and Pro alike — but the attribute it emits,data-generated-stylesheet-hrefs, is consumed only by the Pro client package (packages/react-on-rails-pro/src/ClientSideRenderer.ts, FOUC reveal gating). For OSS users this is pure hot-path waste: in development (Shakapackercache_manifest: false) each call re-reads and JSON-parsesmanifest.jsonper component per request; in production it computes hrefs and emits a JSON attribute that nothing ever reads.Affected code
generated_stylesheet_hrefs_json:react_on_rails/react_on_rails/lib/react_on_rails/pro_helper.rb
Lines 50 to 57 in 5264192
react_on_rails/react_on_rails/lib/react_on_rails/pro_helper.rb
Lines 24 to 25 in 5264192
react_on_rails/react_on_rails/lib/react_on_rails/helper.rb
Lines 536 to 550 in 5264192
generate_component_scriptis called frominternal_react_component):react_on_rails/react_on_rails/lib/react_on_rails/helper.rb
Line 21 in 5264192
react_on_rails/react_on_rails/lib/react_on_rails/helper.rb
Line 802 in 5264192
react_on_rails/packages/react-on-rails-pro/src/ClientSideRenderer.ts
Line 49 in 5264192
react_on_rails/packages/react-on-rails-pro/src/ClientSideRenderer.ts
Lines 132 to 136 in 5264192
Introduced in
PR #4047 ("Fix Pro FOUC reveal gating"), merged 2026-06-15 (commit
af6525d8b). Confirmed viagit log --oneline -S "generated_stylesheet_hrefs_json" 43ed0e138..origin/main— single hit.Mechanism
react_component/react_component_hashcall goes throughinternal_react_component→generate_component_script(render_options)(ProHelperis unconditionally included intoReactOnRails::Helper, so OSS apps run it too).generate_component_scriptcomputes the attribute for every auto-loaded component:preload_sources_for_stylesheet_pack→preload_sources_for_pack→current_shakapacker_instance.manifest.lookup_pack_with_chunks(pack_name, type: :stylesheet). With Shakapacker'scache_manifest: false(the development default), eachlookup_pack_with_chunkscall refreshes the manifest — aFile.read+JSON.parseofmanifest.json— so this happens once per auto-loaded component per request, on top of the lookups the framework already does for pack tags.data-generated-stylesheet-hrefsattribute isClientSideRenderer.tsinpackages/react-on-rails-pro(GENERATED_STYLESHEET_HREFS_ATTRIBUTE, used bygeneratedStylesheetMatchesComponentfor FOUC reveal gating). The OSS client package never reads it (verified by grep acrosspackages/).Failure scenario
Not a correctness failure — a per-render performance/waste regression:
manifest.jsonread+parse cycles (manifest files in real apps are commonly hundreds of KB), inflating the dev feedback loop.to_json, and dead HTML bytes (a JSON array of stylesheet URLs) on every component spec tag — payload that no client code consumes.Impact
auto_load_bundle, plus dead-weight HTML in production responses. No behavior break.Suggested fix
Two independent, compatible changes:
(Helper instances are per-request in Rails, so an ivar cache is request-scoped.)
Test plan
react_on_rails_pro?stubbed false): render an auto-loaded component; assert the spec tag has nodata-generated-stylesheet-hrefsattribute andpreload_sources_for_stylesheet_packis not called.react_on_rails/spec/dummy/spec/helpers/react_on_rails_helper_spec.rbshould keep passing.packages/react-on-rails-pro/tests/ClientSideRenderer.test.tsunaffected.cd react_on_rails && bundle exec rspec spec/dummy/spec/helpers/react_on_rails_helper_spec.rb.Notes
5264192ccby readingpro_helper.rb,helper.rb:526-565, and grepping all ofpackages/for the attribute — consumers are only the ProClientSideRenderer.tsand its test. Confidence: high. The dev-mode re-read claim rests on Shakapacker's documentedcache_manifest: falsebehavior (manifest refreshed on each lookup), not re-measured here.Found in a post-2026-06-12 code review of merged changes (review base 43ed0e1). Filed by Claude Code on behalf of Justin Gordon.