Skip to content

Pro spec/dummy app has severe FOUC (Flash of Unstyled Content) #2100

@justin808

Description

@justin808

Problem

The React on Rails Pro spec/dummy app exhibits a severe Flash of Unstyled Content (FOUC) on initial page load. Users see:

  1. Completely unstyled HTML with no layout/formatting
  2. Layout jump when CSS finally loads and applies
  3. 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:

  1. ✅ CSS is loaded synchronously (good)
  2. But Tailwind classes are defined inline in HTML before CSS loads
  3. async: true on JavaScript means it loads after HTML renders
  4. 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:

  1. HTML renders with these classes
  2. Classes have no styles yet (CSS still loading)
  3. Browser shows unstyled content
  4. 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:

  1. Inline ~50-100 lines of critical layout CSS
  2. Add preload hint for full CSS
  3. Keep async: true on 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:

  1. Run bin/dev in react_on_rails_pro/spec/dummy
  2. Open browser with network throttling (Slow 3G)
  3. 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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions