Having issues with React on Rails? This guide covers the most common problems and their solutions.
Note
Summary for AI agents: Use this page for general React on Rails troubleshooting. For quick fixes to common setup issues, see Common Issues. For Pro-specific issues (streaming SSR, Node renderer, RSC), see Pro Troubleshooting. Always try bundle exec rails react_on_rails:doctor first.
| Problem Area | Quick Check | Go to Section |
|---|---|---|
| Installation | Generator fails or components don't appear | Installation Issues |
| Compilation | Webpack errors, build failures | Build Issues |
| Runtime | Components not rendering, JavaScript errors | Runtime Issues |
| CSS Modules | Styles undefined, SSR CSS crashes | CSS Modules Issues |
| Styling (FOUC) | Unstyled content flash, layout jumps | Flash of Unstyled Content |
| Server Rendering | SSR not working, hydration mismatches | SSR Issues |
| Performance | Slow builds, large bundles, memory issues | Performance Issues |
Error: You have uncommitted changes. Please commit or stash them.
Solution:
git add .
git commit -m "Add react_on_rails gem"
bin/rails generate react_on_rails:installWhy: The generator needs clean git state to show you exactly what it changed.
Error: Node.js not found, npm executable was not detected, or Yarn executable was not detected
Solution:
- Install Node.js 18+ from nodejs.org
- Use the bundled
npm, or install another package manager such aspnpm,yarn, orbun - On macOS, for example:
brew install node pnpm
Error in browser console or webpack output
Solution:
# Make sure the npm package is installed
pnpm add react-on-rails
# or: npm install react-on-rails --save
# or: yarn add react-on-rails
# If using local development with yalc
cd react_on_rails/
yalc publish
cd your_app/
yalc add react-on-railsCheck these common causes:
- Syntax errors in your React components
- Missing dependencies in package.json
- Incorrect imports (check file paths and extensions)
Debug steps:
# Run webpack directly to see detailed errors
bin/webpack
# Or in development mode
bin/webpack --mode developmentError: JavaScript runtime not available
Solution:
# Add to your Gemfile
gem 'execjs'
gem 'mini_racer', platforms: :ruby
# Or use Node.js runtime
export EXECJS_RUNTIME=NodeSymptoms: Empty div or no output where component should be
Check list:
-
Component registered?
import ReactOnRails from 'react-on-rails'; import MyComponent from './MyComponent'; ReactOnRails.register({ MyComponent });
-
Bundle included in view?
<%= javascript_pack_tag 'my-bundle' %> <%= react_component('MyComponent') %>
-
Component exported correctly?
// Use default export export default MyComponent; // Not named export for registration
Error during server-side rendering
Solution: Check your component for browser-only code:
// ❌ Bad - will break SSR
const width = window.innerWidth;
// ✅ Good - check if window exists
const width = typeof window !== 'undefined' ? window.innerWidth : 1200;
// ✅ Better - use useEffect hook
useEffect(() => {
const width = window.innerWidth;
// Use width here
}, []);Symptoms: Component shows initial props but doesn't update
Common causes:
- Caching - Rails fragment caching may cache React components
- Turbo/Turbolinks - Page navigation isn't re-initializing React
- Development mode - Hot reloading not working
Solutions:
<!-- Disable caching for development -->
<% unless Rails.env.development? %>
<% cache do %>
<%= react_component('MyComponent', props: @props) %>
<% end %>
<% else %>
<%= react_component('MyComponent', props: @props) %>
<% end %>There are two common causes of FOUC in React on Rails applications:
Symptoms: Page briefly shows unstyled content before CSS loads, particularly with SSR and auto_load_bundle
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>.
Solution: Use the content_for :body_content pattern to ensure appends happen before the head renders.
See: FOUC Prevention Guide for detailed solutions and examples.
Quick fix:
<% content_for :body_content do %>
<%= react_component "MyComponent", prerender: true %>
<% end %>
<!DOCTYPE html>
<html>
<head>
<%= stylesheet_pack_tag(media: 'all') %>
</head>
<body>
<%= yield :body_content %>
</body>
</html>Symptoms: Layout appears broken or jumps on initial page load—sidebars collapse, flex containers stack vertically, backgrounds are white instead of colored.
Root Cause: When using Tailwind CSS (or similar utility-first frameworks), your layout HTML contains CSS classes like flex, h-screen, bg-slate-100 that have no effect until the CSS bundle loads. The browser renders the raw HTML structure without any styling.
Example of problematic layout:
<!-- These classes do nothing until Tailwind CSS loads -->
<div class="flex flex-row h-screen w-screen">
<div class="flex flex-col bg-slate-100 min-w-[400px]">
<!-- sidebar -->
</div>
<div class="flex-1 overflow-y-auto">
<!-- main content -->
</div>
</div>Solution: Inline critical CSS for layout-affecting properties in the <head> before your main stylesheet loads. Use stable semantic selectors (not Tailwind utility class names) so the critical CSS doesn't drift when you add or remove utility classes.
Step 1: Add semantic classes to your layout's structural elements (alongside existing Tailwind classes):
<body class="app-body bg-white">
<div class="app-shell flex flex-row h-screen w-screen">
<div class="app-sidebar flex flex-col overflow-y-auto p-5 bg-slate-100 ...">
<!-- sidebar content -->
</div>
<div class="app-main flex-1 overflow-x-hidden overflow-y-auto">
<div class="app-main-content p-5">
<!-- main content -->
</div>
</div>
</div>Step 2: Create a critical styles partial (e.g., app/views/layouts/_critical_styles.html.erb) targeting those semantic selectors:
<%#
Critical CSS for preventing FOUC. Uses semantic selectors so it doesn't
need to change when Tailwind utility classes are added or removed.
Only update when the fundamental layout structure changes.
%>
<style data-critical-styles>
.app-body { background-color: #fff; }
.app-shell { display: flex; flex-direction: row; height: 100vh; width: 100vw; }
.app-sidebar {
display: flex; flex-direction: column; overflow-y: auto; padding: 1.25rem;
background-color: #f1f5f9; border-style: solid;
border-right-width: 2px; border-color: #334155;
min-width: 400px; max-width: 400px;
}
.app-main { flex: 1 1 0%; overflow-x: hidden; overflow-y: auto; }
.app-main-content { padding: 1.25rem; }
</style>Step 3: Include it in your layout's <head> before the stylesheet:
<head>
<%= render "layouts/critical_styles" %>
<%= stylesheet_pack_tag('application', media: 'all') %>
</head>Guidelines for critical CSS:
- Use semantic selectors -
.app-shell,.app-sidebar, etc. instead of mirroring Tailwind class names - Keep it minimal - Only define the layout shell (not component styles)
- Focus on layout-affecting properties -
display,flex,width,height,position - Include visible defaults - Background colors and borders that prevent jarring changes
- Add
data-critical-styles- Makes it easy to test that critical styles appear before the bundle
Symptoms:
import css from './Component.module.scss'returnsundefined- SSR crashes:
Cannot read properties of undefined (reading 'className') - Build warning:
export 'default' (imported as 'css') was not found
Root Cause: Shakapacker 9 changed the default CSS Modules configuration from default exports to named exports (namedExport: true).
Solution: Configure CSS loader to use default exports:
// config/webpack/commonWebpackConfig.js
const { generateWebpackConfig } = require('shakapacker');
const commonWebpackConfig = () => {
const baseWebpackConfig = generateWebpackConfig();
baseWebpackConfig.module.rules.forEach((rule) => {
if (rule.use && Array.isArray(rule.use)) {
const cssLoader = rule.use.find((loader) => {
const loaderName = typeof loader === 'string' ? loader : loader?.loader;
return loaderName?.includes('css-loader');
});
if (cssLoader?.options?.modules) {
cssLoader.options.modules.namedExport = false;
cssLoader.options.modules.exportLocalsConvention = 'camelCase';
}
}
});
return baseWebpackConfig;
};See: Rspack Migration Guide for complete configuration details.
Cause: Server-side config overwrites CSS modules settings instead of merging them.
Solution: Preserve existing CSS modules configuration:
// ❌ Wrong
cssLoader.options.modules = { exportOnlyLocals: true };
// ✅ Correct
cssLoader.options.modules = {
...cssLoader.options.modules,
exportOnlyLocals: true,
};Cause: CSS extraction not properly filtered from server bundle. Rspack uses different loader paths than Webpack.
Solution: Filter both Webpack and Rspack CSS extract loaders:
rule.use = rule.use.filter((item) => {
const testValue = typeof item === 'string' ? item : item?.loader;
return !(
testValue?.match(/mini-css-extract-plugin/) ||
testValue?.includes('cssExtractLoader') || // Rspack path
testValue === 'style-loader'
);
});Check:
-
Prerender enabled?
<%= react_component('MyComponent', props: @props, prerender: true) %>
-
JavaScript runtime available?
# Add to Gemfile if missing gem 'mini_racer'
-
No browser-only code in component? (see "window is not defined" above)
Symptoms: React warnings about server/client content differences
Common causes:
- Different props between server and client render
- Browser-only code affecting initial render
- Date/time differences between server and client
Debug:
// Add this to see what props are being used
console.log('Server props:', props);
console.log('Client render time:', new Date());Solutions:
-
Enable caching:
# config/shakapacker.yml development: cache_manifest: true
-
Use webpack-dev-server:
./bin/dev # Uses Procfile.dev with webpack-dev-server -
Check for large dependencies:
yarn why package-name webpack-bundle-analyzer public/packs/manifest.json
Solutions:
-
Code splitting:
// Use dynamic imports const MyComponent = lazy(() => import('./MyComponent'));
-
Check bundle analysis:
ANALYZE=true bin/webpack
-
Remove unused dependencies:
yarn remove unused-package
For a comprehensive debugging guide covering SSR issues, hydration mismatches, and log analysis, see the Debugging Guide.
# config/initializers/react_on_rails.rb
ReactOnRails.configure do |config|
config.logging_on_server = true
config.server_render_method = 'NodeJS' # for better error messages
end# See the final webpack config
bin/webpack --config-dump// In browser console
console.log(ReactOnRails.registeredComponents());- React on Rails version (
bundle list react_on_rails) - Rails version (
rails -v) - Ruby version (
ruby -v) - Node version (
node -v) - Error messages (full stack trace)
- Relevant code snippets
- GitHub Issues - Bug reports and feature requests
- GitHub Discussions - Questions and help
- React + Rails Slack - Real-time community support
- ShakaCode offers consulting and support services
- React on Rails Pro includes priority support
💡 Tip: Most issues are solved by ensuring your setup matches the Quick Start Guide exactly.