A modern full-stack web application demonstrating the integration of Ruby on Rails 8 (8-0-stable branch) with React 19, featuring server-side rendering (SSR), hot module replacement (HMR), and a contemporary asset pipeline.
- Overview
- Ruby Version
- System Dependencies
- Technology Stack
- Why React on Rails & Shakapacker?
- Configuration
- Database
- Installation
- Running the Application
- Testing
- Deployment
- Project Structure
This application showcases a modern approach to building full-stack web applications by combining Rails' robust backend framework with React's powerful frontend capabilities. It uses React on Rails for seamless integration and Shakapacker (the spiritual successor to Webpacker) for asset compilation.
- Ruby 3.4.7 (Latest stable version)
- Managed via
.ruby-versionfile
- Ruby: 3.4.7
- Node.js: 22.20.0+ (managed via npm 10.9.2)
- Rails: 8.0 (8-0-stable branch)
- SQLite3: 2.1+ (development/test databases)
- react_on_rails (16.1.1) - React integration with Rails
- shakapacker (9.2) - Modern webpack integration for Rails
- propshaft - Modern asset pipeline for Rails
- puma - Web server
- tailwindcss-rails - Utility-first CSS framework
- solid_cache, solid_queue, solid_cable - Database-backed adapters for Rails.cache, Active Job, and Action Cable
- foreman (0.90.0) - Process management for development
- kamal - Docker-based deployment tool
- thruster - HTTP asset caching/compression for production
- React: 19.2.0 (Latest version with concurrent features)
- React DOM: 19.2.0
- Webpack: 5.102.1+
- Babel: For JavaScript transpilation
- SWC: Fast Rust-based compiler (alternative to Babel)
- brakeman - Security vulnerability scanner
- rubocop-rails-omakase - Ruby code style enforcement
- debug - Interactive debugging
- capybara & selenium-webdriver - System testing
- Ruby on Rails 8.0 - Modern web application framework
- SQLite3 - Lightweight database (development/test)
- Puma - Multi-threaded web server
- Jbuilder - JSON API builder
- React 19 - Component-based UI library
- React on Rails - Server-side rendering & Rails integration
- Tailwind CSS - Utility-first CSS framework
- Webpack 5 - Module bundler via Shakapacker
- Shakapacker - Webpack integration
- Propshaft - Static asset serving
- Babel/SWC - JavaScript compilation
- PostCSS - CSS processing
- Docker - Containerization (production deployment)
- Kamal - Deployment orchestration
- Thruster - Production web server with caching
React on Rails is the integration layer that allows React components to seamlessly work within a Rails application. Here's why it's essential for this architecture:
-
Server-Side Rendering (SSR)
- Renders React components on the server for faster initial page loads
- Improves SEO by delivering fully-rendered HTML to search engines
- Provides better user experience with immediate content visibility
- Configured via
config.server_bundle_js_fileinreact_on_rails.rb
-
Unified Data Flow
- Props are passed directly from Rails controllers to React components
- Example:
@hello_world_props = { name: "Stranger" }flows seamlessly to React - Uses the
react_component()helper in ERB views:<%= react_component("HelloWorld", props: @hello_world_props, prerender: true) %>
-
Component Registration System
- Automatically registers React components for use in Rails views
- Server bundle (
server-bundle.js) contains all SSR-capable components - Client bundle handles hydration and interactivity
-
Development Experience
- Hot Module Replacement (HMR) for instant updates without page refresh
- React Refresh preserves component state during development
- Separate webpack dev server for optimal development workflow
-
Production Optimization
- Automatic code splitting and lazy loading
- Separate client and server bundles for optimal performance
- Integration with Rails asset pipeline for cache management
Shakapacker is the actively maintained successor to Webpacker, providing modern webpack integration for Rails applications.
-
Active Maintenance
- Webpacker is no longer maintained (archived)
- Shakapacker provides ongoing updates and webpack 5 support
- Community-driven with regular releases
-
Webpack 5 Support
- Latest webpack features and performance improvements
- Module federation support
- Better tree-shaking and code splitting
- Persistent caching for faster builds
-
Flexible Build Options
- Supports multiple compilers: Babel (default), SWC, or esbuild
- Configured via
webpack_loaderinshakapacker.yml - Current setup: Babel with React preset
-
Development Features
- Hot Module Replacement (HMR) via webpack-dev-server
- Live reload capability
- Source maps for debugging
- Fast incremental builds using
mtimestrategy in development
-
Production Optimization
- Content hashing for cache busting
- CSS extraction via mini-css-extract-plugin
- Compression via terser-webpack-plugin
- Subresource integrity (SRI) support for security
-
Rails Integration
- Automatic manifest.json generation for asset lookup
- Seamless integration with Propshaft
- Environment-specific configurations
- Supports Rails 7+ and 8.0
config/webpack/
├── commonWebpackConfig.js # Shared configuration
├── clientWebpackConfig.js # Client-side bundle config
├── serverWebpackConfig.js # SSR bundle config
├── generateWebpackConfigs.js # Dynamic config generation
├── development.js # Dev-specific settings
├── production.js # Production optimizations
└── webpack.config.js # Entry point
This setup enables:
- Dual Bundle System: Separate client and server bundles for optimal SSR
- Conditional Building: Build client-only, server-only, or both based on environment
- HMR Support: Fast refresh during development without full page reloads
- Asset Optimization: Automatic minification, compression, and code splitting
config/application.rb- Main application configurationconfig/environments/- Environment-specific settings (development, test, production)config/database.yml- Database configurationconfig/shakapacker.yml- Webpack/asset pipeline configurationconfig/master.key- Encrypted credentials key (not in version control)
Located in config/initializers/react_on_rails.rb:
- Server bundle:
server-bundle.js(output tossr-generated/) - Test command:
RAILS_ENV=test bin/shakapacker - Supports prerendering with server-side rendering
Managed by Shakapacker in config/webpack/:
- Source path:
client/directory - Entry path:
client/packs/ - Public output:
public/packs/ - Development: HMR enabled on port 3035
- Production: Content hashing, minification, compression
- Configuration:
app/assets/tailwind/ - Build output:
app/assets/builds/tailwind.css - Integrated with the asset pipeline
- Adapter: SQLite3
- Database Files:
- Development:
storage/development.sqlite3 - Test:
storage/test.sqlite3
- Development:
Uses multiple SQLite databases for different Rails 8 features:
- Primary:
storage/production.sqlite3- Main application data - Cache:
storage/production_cache.sqlite3- Solid Cache (database-backed caching) - Queue:
storage/production_queue.sqlite3- Solid Queue (background jobs) - Cable:
storage/production_cable.sqlite3- Solid Cable (WebSocket connections)
bin/rails db:createbin/rails db:migrate
bin/rails db:seed # If seed data existsEnsure Ruby 3.4.7 and Node.js 22+ are installed.
-
Clone the repository
git clone <repository-url> cd ruby-rails-react-demo
-
Run the setup script
bin/setup
This will:
- Install Ruby dependencies (gems)
- Install JavaScript dependencies (npm packages)
- Prepare the database
- Clear logs and temp files
-
Manual setup (alternative)
bundle install npm install bin/rails db:prepare
Uses Foreman to run multiple processes concurrently:
bin/devThis starts three processes:
- Rails server on port 3000
- Webpack dev server (client bundle) with HMR on port 3035
- Webpack watch (server bundle) for SSR
The Procfile.dev defines:
rails: bundle exec rails s -p 3000
wp-client: WEBPACK_SERVE=true bin/shakapacker-dev-server
wp-server: SERVER_BUNDLE_ONLY=yes bin/shakapacker --watch
Access the application at: http://localhost:3000
Static asset compilation (no HMR):
foreman start -f Procfile.dev-static-assetsProduction assets in development:
foreman start -f Procfile.dev-prod-assetsRun in separate terminals:
Terminal 1 - Rails Server:
bundle exec rails server -p 3000Terminal 2 - Client Webpack Dev Server:
WEBPACK_SERVE=true bin/shakapacker-dev-serverTerminal 3 - Server Bundle Watch:
SERVER_BUNDLE_ONLY=yes bin/shakapacker --watchRAILS_ENV=production rails serverEnsure assets are precompiled first:
RAILS_ENV=production rails assets:precompileAll tests:
bin/rails testSystem tests (with browser automation):
bin/rails test:systemSpecific test file:
bin/rails test test/controllers/hello_world_controller_test.rb- Minitest - Rails default testing framework
- Capybara - Integration testing with browser simulation
- Selenium WebDriver - Browser automation for system tests
- Configured in
config/environments/test.rb - Uses
storage/test.sqlite3database - Webpack compilation controlled via
config.build_test_commandin React on Rails
This application includes Docker support for containerized deployment.
Build Docker image:
docker build -t ruby_rails_react_demo .Run container:
docker run -d -p 80:80 \
-e RAILS_MASTER_KEY=<your-master-key> \
--name ruby_rails_react_demo \
ruby_rails_react_demoKamal is included for zero-downtime Docker deployments:
kamal setup # Initial setup
kamal deploy # Deploy updatesConfiguration in config/deploy.yml
-
Asset Precompilation:
RAILS_ENV=production SECRET_KEY_BASE_DUMMY=1 rails assets:precompile
-
Environment Variables:
RAILS_MASTER_KEY- For encrypted credentialsSECRET_KEY_BASE- Session encryptionDATABASE_URL- If using external database
-
Web Server:
- Uses Thruster by default (included with Rails 8)
- Provides HTTP caching, compression, and X-Sendfile
- Production command:
./bin/thrust ./bin/rails server
-
Database Setup:
- Ensure production databases are created
- Run migrations:
RAILS_ENV=production rails db:migrate
.
├── app/
│ ├── assets/ # Static assets (images, stylesheets)
│ ├── controllers/ # Rails controllers
│ ├── models/ # ActiveRecord models
│ ├── views/ # ERB templates
│ │ ├── hello_world/ # Example React-integrated view
│ │ └── layouts/ # Application layouts
│ ├── helpers/ # View helpers
│ ├── jobs/ # Background jobs (Solid Queue)
│ └── mailers/ # Action Mailer classes
│
├── client/ # React application code
│ ├── src/ # React components
│ │ └── HelloWorld/ # Example component
│ │ └── ror_components/
│ │ ├── HelloWorld.client.jsx # Client-side component
│ │ ├── HelloWorld.server.jsx # SSR entry point
│ │ └── HelloWorld.module.css # Component styles
│ ├── packs/ # Webpack entry points
│ │ ├── application.js # Client bundle entry
│ │ └── server-bundle.js # Server bundle entry
│ └── generated/ # Auto-generated React on Rails code
│
├── config/ # Application configuration
│ ├── webpack/ # Webpack configuration files
│ ├── initializers/ # Rails initializers
│ │ └── react_on_rails.rb
│ ├── environments/ # Environment configs
│ ├── application.rb # Main app config
│ ├── routes.rb # Route definitions
│ ├── database.yml # Database config
│ └── shakapacker.yml # Webpack/Shakapacker config
│
├── db/ # Database files and migrations
│ ├── migrate/ # Database migrations
│ └── seeds.rb # Seed data
│
├── public/ # Static files served directly
│ └── packs/ # Compiled webpack assets
│
├── ssr-generated/ # Server-side rendered bundles
│
├── test/ # Test suite
│ ├── controllers/ # Controller tests
│ ├── models/ # Model tests
│ ├── system/ # System/integration tests
│ └── fixtures/ # Test fixtures
│
├── bin/ # Executable scripts
│ ├── dev # Development server launcher
│ ├── setup # Setup script
│ ├── rails # Rails CLI
│ └── shakapacker* # Webpack commands
│
├── Gemfile # Ruby dependencies
├── package.json # JavaScript dependencies
├── Dockerfile # Production container image
├── Procfile.dev # Development processes
└── babel.config.js # Babel configuration
client/: All React/JavaScript code lives here, following React on Rails conventionsconfig/webpack/: Custom webpack configurations for advanced build customizationssr-generated/: Output directory for server-rendered bundlespublic/packs/: Compiled webpack assets with manifest.json for asset lookupapp/views/hello_world/: Demonstrates Rails view with React component integration
class HelloWorldController < ApplicationController
def index
@hello_world_props = { name: "Stranger" }
end
end<h1>Hello World</h1>
<%= react_component("HelloWorld", props: @hello_world_props, prerender: true) %>const HelloWorld = (props) => {
const [name, setName] = useState(props.name);
return (
<div>
<h3>Hello, {name}!</h3>
<input value={name} onChange={(e) => setName(e.target.value)} />
</div>
);
};This demonstrates:
- Server-side rendering (prerender: true)
- Props passing from Rails to React
- Client-side hydration for interactivity
- State management within React
- Solid Queue - Database-backed job processing
- Jobs defined in
app/jobs/ - Configuration:
config/queue.yml
- Solid Cache - Database-backed caching (Rails.cache)
- Configuration:
config/cache.yml
- Solid Cable - Database-backed Action Cable adapter
- Configuration:
config/cable.yml
- Propshaft - Static asset serving
- Shakapacker - JavaScript/CSS compilation via Webpack
- React on Rails Documentation
- Shakapacker Documentation
- Rails 8.0 Guides
- React Documentation
- Tailwind CSS
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is available as open source under the terms of your chosen license.