-
-
Notifications
You must be signed in to change notification settings - Fork 638
Description
Problem
When using auto_load_bundle = true with server-side rendering, there's no straightforward way to prevent FOUC (Flash of Unstyled Content) because stylesheets must load in the <head>, but react_component auto-appends happen during body rendering.
Current Constraint
Shakapacker requires append_stylesheet_pack_tag to be called before stylesheet_pack_tag. With auto_load_bundle = true, react_component automatically calls append_stylesheet_pack_tag during rendering. This creates a chicken-and-egg problem:
- To prevent FOUC,
stylesheet_pack_tagshould be in<head> - But
react_componentcalls in<body>trigger appends after the head has rendered - This violates the "append before main tag" constraint
Current Workaround
The only solution is to use content_for to render the entire body BEFORE the head:
<% content_for :body_content do %>
<%= react_component "NavigationBarApp" %>
<div class="container">
<%= yield %>
</div>
<%= react_component "Footer" %>
<% end %>
<!DOCTYPE html>
<html>
<head>
<%= append_stylesheet_pack_tag('stimulus-bundle') %>
<%= stylesheet_pack_tag(media: 'all') %>
<%= javascript_pack_tag(defer: true) %>
</head>
<body>
<%= yield :body_content %>
</body>
</html>This works but is:
- Non-intuitive (body content defined before
<!DOCTYPE html>) - Not documented anywhere
- Easy to get wrong
- Confusing for developers unfamiliar with the pattern
See working example: shakacode/react-webpack-rails-tutorial#686
Proposed Solutions
Option 1: Document the Pattern
Add clear documentation showing the content_for :body_content pattern for preventing FOUC with SSR and auto_load_bundle.
Option 2: Built-in Helper
Provide a helper that handles this automatically:
<!DOCTYPE html>
<html>
<head>
<%= react_on_rails_head_tags do %>
<%= append_stylesheet_pack_tag('stimulus-bundle') %>
<% end %>
</head>
<body>
<%= react_component "NavigationBarApp" %>
<%= yield %>
</body>
</html>The react_on_rails_head_tags helper would internally:
- Capture and render the body first to collect auto-appends
- Execute the provided block (explicit appends)
- Render stylesheet_pack_tag and javascript_pack_tag
- Return only the head tag content
Option 3: Config for content_for Block Name
Allow react_component with auto_load_bundle to put appends into a named content_for block:
# config/initializers/react_on_rails.rb
config.auto_load_bundle = true
config.auto_load_bundle_target = :head # Appends go into content_for :headThen in layout:
<head>
<%= yield :head %>
<%= stylesheet_pack_tag(media: 'all') %>
</head>
<body>
<%= react_component "MyComponent" %>
</body>Related Issues
This issue affects any SSR application using auto_load_bundle that wants to avoid FOUC by loading stylesheets in the head.
Environment
- react_on_rails: 16.1.1
- shakapacker: 9.1.0
- Rails: 8.0
Related shakapacker documentation issue: shakacode/shakapacker#720