-
-
Notifications
You must be signed in to change notification settings - Fork 638
Open
Labels
Description
Problem
The React on Rails Pro spec/dummy app exhibits a severe Flash of Unstyled Content (FOUC) on initial page load. Users see:
- Completely unstyled HTML with no layout/formatting
- Layout jump when CSS finally loads and applies
- Poor first impression for developers evaluating the Pro features
This is particularly problematic because:
- The dummy app is used to showcase Pro features
- It's the first thing developers see when running
bin/dev - It undermines confidence in the library's production-readiness
Current Setup Issues
Looking at app/views/layouts/application.html.erb:
<%= stylesheet_pack_tag('client-bundle', media: 'all', 'data-turbo-track': 'reload') %>
<%= javascript_pack_tag('client-bundle', 'data-turbo-track': 'reload', async: true) %>Problems:
- ✅ CSS is loaded synchronously (good)
- ❌ But Tailwind classes are defined inline in HTML before CSS loads
- ❌
async: trueon JavaScript means it loads after HTML renders - ❌ No critical CSS inlined in
<head>for instant layout
Why This Happens
The layout has structural HTML with Tailwind classes:
<div class="flex flex-row h-screen w-screen">
<div class="flex flex-col overflow-y-auto p-5 bg-slate-100 border-solid border-r-2 border-slate-700 min-w-[400px] max-w-[400px]">
<!-- sidebar content -->
</div>
<div class="flex-1 overflow-x-hidden overflow-y-auto">
<!-- main content -->
</div>
</div>When the page loads:
- HTML renders with these classes
- Classes have no styles yet (CSS still loading)
- Browser shows unstyled content
- CSS loads and applies → layout jumps
Proposed Solutions
Option 1: Inline Critical CSS (Recommended)
Extract critical layout CSS and inline it in <head>:
<head>
<style>
/* Critical layout CSS */
.flex { display: flex; }
.flex-row { flex-direction: row; }
.h-screen { height: 100vh; }
.w-screen { width: 100vw; }
.bg-slate-100 { background-color: #f1f5f9; }
/* ... other critical styles */
</style>
<%= stylesheet_pack_tag('client-bundle', media: 'all') %>
</head>Pros:
- ✅ Instant layout rendering
- ✅ No FOUC
- ✅ Progressive enhancement
Cons:
⚠️ Requires maintaining critical CSS⚠️ Adds ~1-2KB to HTML
Option 2: Preload CSS
Add CSS preload hint:
<%= stylesheet_link_tag('client-bundle', rel: 'preload', as: 'style') %>
<%= stylesheet_pack_tag('client-bundle', media: 'all') %>Pros:
- ✅ Faster CSS loading
- ✅ Simple to implement
Cons:
⚠️ Still has brief FOUC⚠️ Not as effective as inlining
Option 3: Use defer Instead of async
Change JavaScript loading strategy:
<%= javascript_pack_tag('client-bundle', defer: true) %>Note: This doesn't fix the core CSS issue but may help coordination.
Option 4: Extract Layout CSS to Separate Pack
Create a minimal layout.css pack loaded synchronously before client-bundle:
<%= stylesheet_pack_tag('layout', media: 'all') %>
<%= stylesheet_pack_tag('client-bundle', media: 'all') %>Recommendation
Combine Option 1 + Option 2:
- Inline ~50-100 lines of critical layout CSS
- Add preload hint for full CSS
- Keep
async: trueon JavaScript (correct for React 18 Selective Hydration)
This gives:
- ✅ Zero FOUC - layout renders immediately
- ✅ Fast TTI - JavaScript hydrates asynchronously
- ✅ Professional appearance - great first impression
Testing
To see the FOUC:
- Run
bin/devinreact_on_rails_pro/spec/dummy - Open browser with network throttling (Slow 3G)
- Load any page - notice unstyled flash before CSS applies
Impact
This affects:
- 🎯 Developer experience - First impression of Pro features
- 🎯 Demo quality - Used in sales/marketing materials
- 🎯 Documentation - Screenshots may show FOUC
- 🎯 Example code - Developers may copy this pattern
Files to Modify
react_on_rails_pro/spec/dummy/app/views/layouts/application.html.erb- Possibly extract critical CSS generation script
- Update webpack config if going with Option 4