Why Combine React with Ruby on Rails
Rails and React remain two of the most popular choices for building web applications in 2026. While Rails provides an exceptional backend foundation for rapid development with clean architecture conventions, React excels at creating dynamic, interactive user interfaces through its component-based approach.
The combination of these technologies gives developers the best of both worlds: Rails' developer productivity with its convention-over-configuration philosophy, and React's component-based frontend excellence that enables reusable, maintainable user interface code. This pairing has proven particularly effective for applications requiring real-time updates, complex state management, or sophisticated user interactions while maintaining a robust, scalable backend architecture.
This guide explores the most effective approaches for integrating React with Ruby on Rails, covering everything from initial setup with Rails 8 and esbuild to advanced patterns that help you build modern, performant web applications that scale gracefully under increasing load. Understanding how React handles reactivity will help you make the most of these integration strategies.
Why Rails continues to dominate web application development
Convention over Configuration
Accelerates development by reducing decision fatigue while maintaining code quality through the DRY principle.
Performance Under Load
Architecture that accommodates rising traffic volumes and larger datasets seamlessly.
Extensive Gem Ecosystem
Over 175,000+ free libraries extending core functionality for authentication, file uploads, and more.
Built-in Security
Protection against XSS, CSRF, and SQL injection with Rails 8's enhanced authentication tools.
The component-based approach for modern user interfaces
Component-Based Architecture
Break down UIs into smaller, reusable components that can be shared across your application.
Virtual DOM Performance
Updates only the parts of the UI that change, making rendering faster and more efficient.
Declarative Syntax
Describe how the UI should look rather than providing exact steps for easier debugging.
SEO Benefits
Rapid rendering contributes to superior page speed metrics that search engines value highly.
Integration Approaches
API-Based (Headless) Integration
Build two standalone applications: a React-based frontend and a Rails backend serving as an API. These communicate via RESTful or GraphQL requests.
Best for: Teams with specialized developers, mobile app backends, multi-frontend applications
Example API endpoint structure:
# Rails API controller
class Api::EventsController < ApplicationController
def index
render json: Event.includes(:venue).all,
include: [:venue]
end
end
// React data fetching
const [events, setEvents] = useState([]);
useEffect(() => {
fetch('/api/events')
.then(res => res.json())
.then(data => setEvents(data));
}, []);
Library Integration
Connect React as a library within your Rails application, eliminating the need for a separate API.
Best for: Smaller applications, incremental adoption, faster time-to-market
<%# Rails view with React component %>
<div id="react-calendar"
data-events="<%= @events.to_json %>">
</div>
Server-Side Rendering (SSR) Hybrid
Use Next.js for the React frontend with Rails as a headless CMS/API backend. This approach is particularly valuable when initial page load performance and SEO are critical priorities. Explore how static site generation fits into your SSR strategy.
Best for: SEO-critical applications, better initial page loads
// Next.js getServerSideProps
export async function getServerSideProps(context) {
const events = await fetch('https://api.example.com/events')
.then(res => res.json());
return { props: { events } };
}
Setting Up React with Rails 8
Prerequisites
- Ruby 3.4+
- Rails 8.0+
- PostgreSQL 17+
- Yarn 1.22+
- esbuild as JavaScript bundler
Creating Your Application
rails new rails_react_app -d postgresql -j esbuild -c bootstrap -T
The -d flag specifies the database while -j configures esbuild for JavaScript bundling. The -c bootstrap option includes Bootstrap CSS framework, and -T skips Test::Unit for RSpec compatibility.
Removing Import Maps
For comprehensive React support, transition from importmap-rails to a full bundler setup. Modern Rails applications often start with importmap-rails for managing JavaScript dependencies, but removing import maps ensures consistent JavaScript compilation and React compatibility.
# Remove the importmap-rails gem
bundle remove importmap-rails
# Update manifest.js - remove importmap links
# Clear application.js of importmap directives
# Update application.html.erb to remove javascript_importmap_tags
# Remove config/importmap.rb and bin/importmap script
Installing jsbundling-rails
After removing import maps, install jsbundling-rails to manage JavaScript compilation:
bundle add jsbundling-rails
bin/rails javascript:install:esbuild
Esbuild has proven to be the simplest bundler option, requiring no additional configuration to get React working properly. It provides fast compilation and excellent tree-shaking capabilities out of the box.
1# Install React dependencies2yarn add react react-dom3 4# Reinstall Turbo and Stimulus for esbuild compatibility5bin/rails turbo:install6bin/rails stimulus:install7 8# Create your first React component9mkdir -p app/javascript/components10 11# Start the development server12bin/devWorking with Existing Rails Applications
Incremental Adoption Strategy
For teams working with established Rails applications, the "all-or-nothing" perception of React integration is a misconception. These technologies integrate on a spectrum, allowing gradual adoption rather than complete rewrites. Consider a real-world scenario where you need to add an interactive dashboard component to an existing Rails application. Instead of rebuilding the entire frontend, you can selectively integrate React components where they provide the most value.
This incremental approach lets you modernize specific features while leaving stable, well-functioning sections of your application unchanged. The key is identifying which parts of your user interface would benefit most from React's dynamic capabilities--typically areas with complex state management or real-time updates.
Turbo Drive Compatibility
Turbo Drive replaces page contents during navigation. React components won't automatically reinitialize after navigation. Handle this by listening for the turbo:load event before mounting components and the turbo:before-visit event to unmount them when navigating away:
import { createRoot } from 'react-dom/client';
import App from './components/App';
document.addEventListener("turbo:load", () => {
const app = document.getElementById("react-dashboard");
if (app) {
const root = createRoot(app);
root.render(<App />);
// Clean up when navigating away
document.addEventListener("turbo:before-visit", () => {
root.unmount();
});
}
});
This pattern ensures React components properly initialize and clean up during Turbo Drive navigation, preventing memory leaks and rendering issues that could degrade performance over time.
Passing Data from Rails to React
Rails views can pass data to React components through several approaches. For smaller datasets, embedding data directly in the page via data attributes or inline JSON script tags works efficiently:
<%# In your Rails view - embed data directly %>
<script id="user-analytics" type="application/json">
<%= raw @user_analytics.to_json %>
</script>
<div id="analytics-dashboard"
data-current-user="<%= current_user.id %>">
</div>
// In your React component
const dashboard = document.getElementById('analytics-dashboard');
const analyticsData = JSON.parse(
document.getElementById('user-analytics').textContent
);
function Dashboard() {
const userId = dashboard.dataset.currentUser;
return <AnalyticsPanel userId={userId} data={analyticsData} />;
}
Performance Optimization
Bundle Size Management
Esbuild's fast compilation times and efficient tree-shaking help keep bundle sizes manageable. Monitor your dependencies and identify large packages that could be optimized or replaced with lighter alternatives. The esbuild bundler produces highly optimized output with minimal configuration, making it an excellent choice for React + Rails applications.
Key strategies include:
- Analyze bundle composition - Use tools to identify large dependencies
- Audit third-party libraries - Replace heavy libraries with lighter alternatives when possible
- Remove unused code - Tree-shaking automatically eliminates dead code
Component Lazy Loading
For larger React applications, implement code splitting to load components only when needed. This reduces initial bundle size and improves page load times significantly:
import { lazy, Suspense } from 'react';
// Code split at the component level
const DataVisualization = lazy(() =>
import('./components/DataVisualization')
);
function Dashboard() {
return (
<Suspense fallback={<LoadingSpinner />}>
<DataVisualization />
</Suspense>
);
}
Server-Side Rendering Considerations
When using SSR with Next.js and Rails, ensure proper hydration strategies to prevent the double-rendering issue where server-rendered HTML conflicts with client-side React initialization. Use React's hydrateRoot method with appropriate error boundaries:
import { hydrateRoot, Root } from 'react-dom/client';
const rootElement = document.getElementById('root');
hydrateRoot(
rootElement,
<Root />
);
Caching Strategies
Leverage Rails' fragment caching to cache expensive React component renders while allowing dynamic sections to update independently. This combines Rails' server-side caching with React's client-side updates:
<%# Cache the static parts of the React component %>
<%= cache @cache_key do %>
<div id="cached-component"
data-props="<%= @dynamic_props.to_json %>">
</div>
<% end %>
This hybrid approach ensures that expensive server-side computations are cached while still allowing client-side interactivity to function normally.
Best Practices for Production
Environment Configuration
Configure separate JavaScript entry points for development and production environments. Production builds should minify and compress assets while development builds should preserve source maps for debugging:
// config/environments/production.js
production: {
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js',
},
module: {
rules: [
{
test: /\.jsx$/,
use: ['esbuild-loader'],
},
],
},
}
Testing Strategies
Implement comprehensive testing for both React and Rails components:
- Component testing with React Testing Library or Vitest for React components
- Rails integration tests for API endpoints ensuring data integrity
- End-to-end testing with Playwright or Cypress for user workflows
This dual testing approach catches both frontend component issues and backend data handling problems before they reach production.
Deployment Checklist
For containerized deployments, build React assets during the image build process to avoid compilation overhead during runtime:
# Build stage
FROM ruby:3.4-slim as build
WORKDIR /app
COPY package*.json ./
RUN yarn install --production
COPY . .
RUN bundle install
RUN bin/rails assets:precompile
# Production stage
FROM ruby:3.4-slim
WORKDIR /app
COPY --from=build /app/public ./public
COPY --from=build /app Gemfile Gemfile.lock ./
RUN bundle install
EXPOSE 3000
CMD ["bin/rails", "server", "-b", "0.0.0.0"]
When to Choose Each Approach
| Approach | Best For | Trade-offs |
|---|---|---|
| API-Based | Specialized teams, mobile apps, complex scaling needs | Requires API design discipline |
| Library Integration | Smaller apps, faster time-to-market, full-stack teams | Tighter coupling between layers |
| SSR Hybrid | SEO-critical apps, initial performance priority | More complex deployment pipeline |
For teams building custom web applications that require both backend robustness and frontend interactivity, library integration often provides the fastest path to production while maintaining flexibility for future architectural evolution.
Frequently Asked Questions
Conclusion
The combination of React and Ruby on Rails provides a powerful, flexible foundation for modern web applications. Whether building new or incrementally modernizing existing applications, these technologies work together effectively when integrated thoughtfully.
Choose the right approach for your specific needs: start with library integration for faster time-to-market, progress to API-based architecture as your application scales, or implement SSR hybrid solutions when SEO and initial load performance are critical. Follow established patterns for component mounting and Turbo compatibility, and implement performance optimizations that keep your application responsive and scalable.
With Rails 8's enhanced features and React's mature ecosystem, developers have more options than ever for building applications that balance development velocity with long-term maintainability. The key is thoughtful integration that plays to each technology's strengths while avoiding unnecessary complexity.
Ready to modernize your web application? Our team specializes in full-stack web development solutions that leverage the best of Rails and React for high-performance, scalable applications.