Skip to content

Commit 2b5bab8

Browse files
authored
Document content_for pattern to prevent FOUC with SSR and auto_load_bundle (#1865)
Fixes #1864
1 parent b3ce154 commit 2b5bab8

File tree

2 files changed

+82
-19
lines changed

2 files changed

+82
-19
lines changed

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

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -522,23 +522,58 @@ 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**: When using `auto_load_bundle = true`, `react_component` calls automatically invoke `append_stylesheet_pack_tag` during rendering. However, Shakapacker requires these appends to execute BEFORE the main `stylesheet_pack_tag` in your layout's `<head>`. Since Rails renders the layout's `<head>` before the `<body>` (where `react_component` calls typically occur), the appends happen too late, causing FOUC.
528528

529-
**Solutions**:
529+
**Solution**: Use the `content_for :body_content` pattern documented in Shakapacker's [Preventing FOUC guide](https://github.com/shakacode/shakapacker/blob/master/docs/preventing_fouc.md#the-content_for-body_content-pattern).
530+
531+
This pattern renders your body content first, ensuring all `react_component` auto-appends execute before the `<head>` renders:
532+
533+
```erb
534+
<%# Step 1: This block executes first, capturing content AND triggering auto-appends %>
535+
<% content_for :body_content do %>
536+
<%= react_component "NavigationBarApp", prerender: true %>
537+
538+
<div class="container">
539+
<%= yield %>
540+
</div>
541+
542+
<%= react_component "Footer", prerender: true %>
543+
<% end %>
544+
<!DOCTYPE html>
545+
<html>
546+
<head>
547+
<%= csrf_meta_tags %>
548+
<%= csp_meta_tag %>
549+
550+
<%# Step 2: Head renders with all accumulated stylesheet/JS appends %>
551+
<%= stylesheet_pack_tag(media: 'all') %>
552+
<%= javascript_pack_tag(defer: true) %>
553+
</head>
554+
<body>
555+
<%# Step 3: Finally, the captured body_content is rendered here %>
556+
<%= yield :body_content %>
557+
</body>
558+
</html>
559+
```
560+
561+
**Note**: While defining body content before `<!DOCTYPE html>` may seem counter-intuitive, Rails processes the `content_for` block first (capturing content and triggering appends), then renders the HTML in proper document order.
562+
563+
**Alternative**: If the `content_for` pattern doesn't fit your needs, disable auto-loading and manually specify packs:
564+
565+
```ruby
566+
# config/initializers/react_on_rails.rb
567+
config.auto_load_bundle = false
568+
```
530569

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
533-
- **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';`
570+
**Additional Resources**:
536571

537-
**Key insight**: Choose your development mode based on your current needs:
572+
- **Complete FOUC prevention guide**: [Shakapacker Preventing FOUC documentation](https://github.com/shakacode/shakapacker/blob/master/docs/preventing_fouc.md)
573+
- **Working example**: [react-webpack-rails-tutorial PR #686](https://github.com/shakacode/react-webpack-rails-tutorial/pull/686)
574+
- **Related issue**: [Shakapacker #720](https://github.com/shakacode/shakapacker/issues/720)
538575

539-
- Use HMR for fastest development (accept FOUC)
540-
- Use static mode when testing styling without FOUC
541-
- Use production mode for final testing
576+
**Note**: HMR-related FOUC in development mode (dynamic CSS injection) is separate from this SSR auto-loading issue. See Shakapacker docs for details.
542577

543578
#### 3. "document is not defined" errors during SSR
544579

docs/deployment/troubleshooting.md

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ Having issues with React on Rails? This guide covers the most common problems an
66

77
### Is your issue with...?
88

9-
| Problem Area | Quick Check | Go to Section |
10-
| -------------------- | ------------------------------------------- | -------------------------------------------- |
11-
| **Installation** | Generator fails or components don't appear | [Installation Issues](#-installation-issues) |
12-
| **Compilation** | Webpack errors, build failures | [Build Issues](#-build-issues) |
13-
| **Runtime** | Components not rendering, JavaScript errors | [Runtime Issues](#-runtime-issues) |
14-
| **Server Rendering** | SSR not working, hydration mismatches | [SSR Issues](#-server-side-rendering-issues) |
15-
| **Performance** | Slow builds, large bundles, memory issues | [Performance Issues](#-performance-issues) |
9+
| Problem Area | Quick Check | Go to Section |
10+
| -------------------- | ------------------------------------------- | ------------------------------------------------------------ |
11+
| **Installation** | Generator fails or components don't appear | [Installation Issues](#-installation-issues) |
12+
| **Compilation** | Webpack errors, build failures | [Build Issues](#-build-issues) |
13+
| **Runtime** | Components not rendering, JavaScript errors | [Runtime Issues](#-runtime-issues) |
14+
| **Styling (FOUC)** | Unstyled content flash with SSR | [Flash of Unstyled Content](#flash-of-unstyled-content-fouc) |
15+
| **Server Rendering** | SSR not working, hydration mismatches | [SSR Issues](#-server-side-rendering-issues) |
16+
| **Performance** | Slow builds, large bundles, memory issues | [Performance Issues](#-performance-issues) |
1617

1718
## 🚨 Installation Issues
1819

@@ -164,6 +165,33 @@ useEffect(() => {
164165
<% end %>
165166
```
166167

168+
### "Flash of Unstyled Content (FOUC)"
169+
170+
**Symptoms:** Page briefly shows unstyled content before CSS loads, particularly with SSR and `auto_load_bundle`
171+
172+
**Root Cause:** When using `auto_load_bundle = true` with server-side rendering, `react_component` calls trigger `append_stylesheet_pack_tag` during body rendering, but these appends must execute BEFORE the `stylesheet_pack_tag` in the `<head>`.
173+
174+
**Solution:** Use the `content_for :body_content` pattern to ensure appends happen before the head renders.
175+
176+
**See:** [FOUC Prevention Guide](../core-concepts/auto-bundling-file-system-based-automated-bundle-generation.md#2-css-not-loading-fouc---flash-of-unstyled-content) for detailed solutions and examples.
177+
178+
**Quick fix:**
179+
180+
```erb
181+
<% content_for :body_content do %>
182+
<%= react_component "MyComponent", prerender: true %>
183+
<% end %>
184+
<!DOCTYPE html>
185+
<html>
186+
<head>
187+
<%= stylesheet_pack_tag(media: 'all') %>
188+
</head>
189+
<body>
190+
<%= yield :body_content %>
191+
</body>
192+
</html>
193+
```
194+
167195
## 🖥️ Server-Side Rendering Issues
168196

169197
### "Server rendering not working"

0 commit comments

Comments
 (0)