This demo showcases React on Rails v15's file-system-based automated bundle generation with intelligent bundle splitting and SSR support.
React on Rails automatically detects components using a conventional directory structure:
app/javascript/src/
├── HelloWorld/ror_components/
│ ├── HelloWorld.jsx # Lightweight component (10.0KB JS + 2.5KB CSS)
│ └── HelloWorld.module.css # CSS modules
└── HeavyMarkdownEditor/ror_components/
├── HeavyMarkdownEditor.jsx # Heavy component (26.5KB + 1,081KB libraries)
└── HeavyMarkdownEditor.module.css
config/initializers/react_on_rails.rb:
config.components_subdirectory = "ror_components"
config.auto_load_bundle = true
config.server_bundle_js_file = "server-bundle.js"config/shakapacker.yml:
nested_entries: true # Enable nested directory scanningrake react_on_rails:generate_packsscans the directory structure- Webpack entries are automatically generated in
app/javascript/packs/generated/ - Bundle splitting separates lightweight from heavyweight dependencies
- Server bundle is created for SSR with component registration
- Bundle Size: ~50KB (React basics only)
- Loading: Immediate SSR + instant hydration
- Use Case: Core UI components, forms, navigation
- Bundle Size: ~2.7MB (58+ dependencies via react-markdown)
- Loading: SSR skeleton → progressive enhancement
- Use Case: Rich editors, data visualization, third-party widgets
📋 SSR + Heavy Components: For detailed implementation patterns, including FOUC prevention and dynamic imports, see SSR_DYNAMIC_IMPORTS_GUIDE.md.
Webpack automatically creates shared vendor chunks:
- runtime.js (50KB) # Webpack runtime
- vendors-react.js (530KB) # React core (shared)
- vendors-css.js (1.06MB) # CSS processing (shared)
- vendors-markdown.js (1.03MB) # Heavy deps (HeavyMarkdownEditor only)
- HelloWorld.js (31KB) # Component code
- HeavyMarkdownEditor.js (40KB) # Component code
| Component | Total Size | Network Requests | Load Time |
|---|---|---|---|
| HelloWorld | 612KB | 3 chunks | ~100ms |
| HeavyMarkdownEditor | 2.7MB | 5 chunks | ~300ms |
- Shared vendors cached across pages
- Component isolation - adding HeavyMarkdownEditor doesn't affect HelloWorld
- Incremental loading - users only download what they need
Both components support SSR with different strategies:
<%= react_component("HelloWorld", props: @props, prerender: true) %>- Complete server rendering
- Immediate hydration
- Zero client-side loading states
<%= react_component("HeavyMarkdownEditor", props: @props, prerender: true) %>- Server renders structure + skeleton
- Client progressively enhances with heavy dependencies
- Prevents FOUC with content-aware placeholders
- Create directory:
app/javascript/src/NewComponent/ror_components/ - Add component:
NewComponent.jsx - Run:
bundle exec rake react_on_rails:generate_packs - Use in views:
<%= react_component("NewComponent") %>
- No webpack entries to maintain
- No manual
ReactOnRails.register()calls - No bundle configuration needed
- Automatic CSS modules support
- Critical components (HelloWorld) load immediately
- Enhancement components (HeavyMarkdownEditor) load progressively
- Vendor chunks are cached aggressively
- Server rendering provides fast initial paint
The generated packs in app/javascript/packs/generated/ should be:
- Re-generated during deployments.
Track bundle sizes and loading performance:
- Monitor chunk sizes in webpack-bundle-analyzer
- Track component hydration times
- Measure cumulative layout shift (CLS) for FOUC prevention
This architecture scales to hundreds of components:
- Automatic bundling prevents webpack configuration explosion
- Intelligent splitting keeps bundles appropriately sized
- Progressive loading maintains performance as complexity grows
- SSR flexibility allows per-component optimization strategies
- SSR + Dynamic Imports Guide - Technical deep-dive on handling heavy components with SSR
- React on Rails Docs - Official documentation for React on Rails gem