Skip to content

Commit a7f765a

Browse files
justin808claude
andcommitted
Document content_for pattern to prevent FOUC with SSR and auto_load_bundle
Fixes #1864 When using auto_load_bundle = true with server-side rendering, there's a chicken-and-egg problem: stylesheets must load in the <head>, but react_component calls in <body> trigger auto-appends after the head has rendered, causing FOUC (Flash of Unstyled Content). This commit documents the content_for workaround pattern that ensures all react_component calls happen before the head renders, preventing FOUC. Key improvements: - Explains the root cause of FOUC with SSR + auto_load_bundle - Documents the content_for :body_content pattern with full example - Provides alternative solutions (disable auto_load_bundle, manual packs) - Clarifies that HMR FOUC is separate from this SSR issue - References working example in react-webpack-rails-tutorial PR #686 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent b3ce154 commit a7f765a

File tree

1 file changed

+63
-10
lines changed

1 file changed

+63
-10
lines changed

docs/core-concepts/auto-bundling-file-system-based-automated-bundle-generation.md

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -522,23 +522,76 @@ As of version 13.3.4, bundles inside directories that match `config.components_s
522522

523523
#### 2. CSS not loading (FOUC - Flash of Unstyled Content)
524524

525-
**Problem**: Components load but CSS styles are missing or delayed.
525+
**Problem**: Components load but CSS styles are missing or delayed, particularly with server-side rendering and `auto_load_bundle = true`.
526526

527-
**Important**: FOUC (Flash of Unstyled Content) **only occurs with HMR (Hot Module Replacement)**. Static and production modes work perfectly without FOUC.
527+
**Root Cause with SSR + auto_load_bundle**: Shakapacker requires `append_stylesheet_pack_tag` to be called before `stylesheet_pack_tag` renders. However, with SSR and `auto_load_bundle = true`, `react_component` calls in the `<body>` automatically call `append_stylesheet_pack_tag`, but this happens AFTER the `<head>` (which contains `stylesheet_pack_tag`) has already been rendered. This creates a chicken-and-egg problem causing FOUC.
528528

529529
**Solutions**:
530530

531-
- **Development with HMR** (`./bin/dev`): FOUC is expected behavior due to dynamic CSS injection - **not a bug**
532-
- **Development static** (`./bin/dev static`): No FOUC - CSS is extracted to separate files like production
531+
**Option 1: Use content_for pattern (Recommended for SSR + auto_load_bundle)**
532+
533+
Render your body content BEFORE the head using Rails `content_for` blocks. This ensures all `react_component` calls (and their auto-appends) happen before the head renders:
534+
535+
```erb
536+
<% content_for :body_content do %>
537+
<%= react_component "NavigationBarApp", prerender: true %>
538+
539+
<div class="container">
540+
<%= yield %>
541+
</div>
542+
543+
<%= react_component "Footer", prerender: true %>
544+
<% end %>
545+
<!DOCTYPE html>
546+
<html>
547+
<head>
548+
<%= csrf_meta_tags %>
549+
<%= csp_meta_tag %>
550+
551+
<!-- All auto-appended stylesheets will be included here -->
552+
<%= stylesheet_pack_tag(media: 'all') %>
553+
<%= javascript_pack_tag(defer: true) %>
554+
</head>
555+
<body>
556+
<%= yield :body_content %>
557+
</body>
558+
</html>
559+
```
560+
561+
**Note**: While this pattern may seem counter-intuitive (body content before `<!DOCTYPE html>`), it ensures proper stylesheet loading order and prevents FOUC.
562+
563+
**Option 2: Disable auto_load_bundle and manually manage packs**
564+
565+
If the `content_for` pattern doesn't fit your architecture, set `auto_load_bundle = false` and manually manage pack loading:
566+
567+
```ruby
568+
# config/initializers/react_on_rails.rb
569+
config.auto_load_bundle = false
570+
```
571+
572+
Then in your layout:
573+
574+
```erb
575+
<head>
576+
<%= stylesheet_pack_tag 'application' %>
577+
</head>
578+
<body>
579+
<%= react_component "MyComponent", prerender: true %>
580+
</body>
581+
```
582+
583+
**Option 3: HMR-related FOUC (Development Only)**
584+
585+
With HMR (Hot Module Replacement) in development mode, FOUC is expected behavior due to dynamic CSS injection:
586+
587+
- **Development with HMR** (`./bin/dev`): FOUC is expected - CSS is dynamically injected
588+
- **Development static** (`./bin/dev static`): No FOUC - CSS is extracted to separate files
533589
- **Production** (`./bin/dev prod`): No FOUC - CSS is extracted and optimized
534-
- **Layout**: Verify your layout includes empty `<%= stylesheet_pack_tag %>` placeholder for CSS injection
535-
- **Component imports**: Check that CSS files are properly imported: `import styles from './Component.module.css';`
536590

537-
**Key insight**: Choose your development mode based on your current needs:
591+
**Additional checks**:
538592

539-
- Use HMR for fastest development (accept FOUC)
540-
- Use static mode when testing styling without FOUC
541-
- Use production mode for final testing
593+
- **Layout**: Verify your layout includes empty `<%= stylesheet_pack_tag %>` placeholder for CSS injection
594+
- **Component imports**: Check that CSS files are properly imported: `import styles from './Component.module.css';`
542595

543596
#### 3. "document is not defined" errors during SSR
544597

0 commit comments

Comments
 (0)