
How to Set Up a Ruby on Rails v7 Project with React Frontend on Ubuntu 24
Setting up a modern web application with Ruby on Rails 7 and React on Ubuntu 24 is becoming increasingly popular as developers look to combine Rails’ robust backend capabilities with React’s dynamic frontend experience. This guide will walk you through creating a full-stack application that leverages Rails 7’s new features like Importmap and Hotwire, while integrating a React frontend for maximum flexibility. You’ll learn how to configure both frameworks, handle data flow between them, and deploy everything on Ubuntu 24’s latest environment.
How Rails 7 and React Integration Works
Rails 7 introduced significant changes to how frontend assets are handled, moving away from Webpacker to embrace modern JavaScript tooling. When integrating React, you have several architectural choices:
- Full separation: Rails as pure API backend, React as standalone frontend
- Hybrid approach: Rails views for some pages, React components for interactive features
- Single Page Application: Rails serves the initial React app, handles routing via React Router
The most flexible approach involves setting up Rails as an API-first backend while serving a React frontend from the same domain. This maintains the benefits of Rails’ conventions while giving you React’s component ecosystem.
Integration Method | Complexity | Performance | SEO Friendly | Best For |
---|---|---|---|---|
Full Separation | Medium | High | Requires SSR | Large applications |
Hybrid Approach | High | Medium | Yes | Gradual migration |
SPA with Rails API | Low | High | Requires SSR | Modern web apps |
Prerequisites and System Setup
Before diving into the setup, ensure your Ubuntu 24 system has the necessary dependencies. Ubuntu 24’s package repositories include updated versions of most tools we’ll need.
# Update system packages
sudo apt update && sudo apt upgrade -y
# Install essential build tools
sudo apt install -y curl wget gnupg2 software-properties-common build-essential
# Install Node.js 20 (recommended for React)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# Install Ruby version manager (rbenv)
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
source ~/.bashrc
# Install ruby-build plugin
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
Install Ruby 3.2.0 (latest stable version supported by Rails 7):
# Install Ruby dependencies
sudo apt install -y libssl-dev libreadline-dev zlib1g-dev autoconf bison libyaml-dev libreadline-dev libncurses5-dev libffi-dev libgdbm-dev
# Install Ruby 3.2.0
rbenv install 3.2.0
rbenv global 3.2.0
# Verify installation
ruby -v
gem -v
Setting Up Rails 7 Backend
Rails 7 ships with several new defaults that make API development smoother. We’ll create a Rails application configured primarily as an API with some view capabilities for serving our React frontend.
# Install Rails 7
gem install rails -v "~> 7.0"
# Create new Rails project
rails new my_rails_react_app --api --database=postgresql
cd my_rails_react_app
# Add necessary gems to Gemfile
echo "
gem 'rack-cors'
gem 'jbuilder'
gem 'image_processing', '~> 1.2'
" >> Gemfile
bundle install
Configure CORS for React frontend communication:
# config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'localhost:3001', '127.0.0.1:3001' # React dev server
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
credentials: true
end
end
Create a sample API controller to test the setup:
# app/controllers/api/v1/posts_controller.rb
class Api::V1::PostsController < ApplicationController
before_action :set_post, only: [:show, :update, :destroy]
def index
@posts = Post.all
render json: @posts
end
def show
render json: @post
end
def create
@post = Post.new(post_params)
if @post.save
render json: @post, status: :created
else
render json: @post.errors, status: :unprocessable_entity
end
end
def update
if @post.update(post_params)
render json: @post
else
render json: @post.errors, status: :unprocessable_entity
end
end
def destroy
@post.destroy
head :no_content
end
private
def set_post
@post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :content, :author)
end
end
Generate the Post model and run migrations:
# Generate model
rails generate model Post title:string content:text author:string
# Setup database (install PostgreSQL if needed)
sudo apt install -y postgresql postgresql-contrib libpq-dev
sudo systemctl start postgresql
sudo systemctl enable postgresql
# Create database user
sudo -u postgres createuser -s $(whoami)
sudo -u postgres createdb $(whoami)
# Run migrations
rails db:create
rails db:migrate
Update routes for API endpoints:
# config/routes.rb
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :posts
end
end
# Serve React app for all other routes
get '*path', to: 'application#fallback_index_html', constraints: ->(request) do
!request.xhr? && request.format.html?
end
end
React Frontend Setup
We'll create the React frontend in a separate directory within our Rails project to keep things organized while maintaining the ability to serve everything from one domain in production.
# Create React app in frontend directory
npx create-react-app frontend --template typescript
cd frontend
# Install additional dependencies
npm install axios react-router-dom @types/react-router-dom
# Install development dependencies
npm install -D @types/axios concurrently
Configure the React development server to run on port 3001 to avoid conflicts with Rails:
# frontend/package.json
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
// ... existing dependencies
},
"scripts": {
"start": "PORT=3001 react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"proxy": "http://localhost:3000"
}
Create a service for API communication:
// frontend/src/services/api.ts
import axios from 'axios';
const API_BASE_URL = process.env.NODE_ENV === 'production'
? '/api/v1'
: 'http://localhost:3000/api/v1';
const api = axios.create({
baseURL: API_BASE_URL,
headers: {
'Content-Type': 'application/json',
},
withCredentials: true,
});
export interface Post {
id: number;
title: string;
content: string;
author: string;
created_at: string;
updated_at: string;
}
export const postsAPI = {
getAll: () => api.get('/posts'),
getById: (id: number) => api.get(`/posts/${id}`),
create: (post: Omit) =>
api.post('/posts', { post }),
update: (id: number, post: Partial) =>
api.put(`/posts/${id}`, { post }),
delete: (id: number) => api.delete(`/posts/${id}`),
};
export default api;
Create a React component that interacts with the Rails API:
// frontend/src/components/PostsList.tsx
import React, { useState, useEffect } from 'react';
import { postsAPI, Post } from '../services/api';
const PostsList: React.FC = () => {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchPosts();
}, []);
const fetchPosts = async () => {
try {
setLoading(true);
const response = await postsAPI.getAll();
setPosts(response.data);
} catch (err) {
setError('Failed to fetch posts');
console.error('Error fetching posts:', err);
} finally {
setLoading(false);
}
};
const handleDelete = async (id: number) => {
try {
await postsAPI.delete(id);
setPosts(posts.filter(post => post.id !== id));
} catch (err) {
setError('Failed to delete post');
}
};
if (loading) return Loading...;
if (error) return Error: {error};
return (
Posts
{posts.length === 0 ? (
No posts found.
) : (
{posts.map(post => (
-
{post.title}
By: {post.author}
{post.content}
))}
)}
);
};
export default PostsList;
Production Build and Deployment Setup
For production, we need to configure Rails to serve the React build files and handle routing properly.
First, update the Rails application controller to handle the React app fallback:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
def fallback_index_html
render file: Rails.root.join('public', 'index.html')
end
end
Create a build script that integrates React build with Rails:
# bin/build_frontend
#!/bin/bash
set -e
echo "Building React frontend..."
cd frontend
npm run build
echo "Copying build files to Rails public directory..."
cd ..
rm -rf public/static
cp -r frontend/build/* public/
cp frontend/build/index.html app/views/layouts/
echo "Frontend build complete!"
Make the script executable:
chmod +x bin/build_frontend
Create a development script to run both servers concurrently:
# package.json (in project root)
{
"name": "rails-react-app",
"scripts": {
"dev": "concurrently \"rails server\" \"cd frontend && npm start\"",
"build": "./bin/build_frontend",
"start": "rails server"
},
"devDependencies": {
"concurrently": "^7.6.0"
}
}
Common Issues and Troubleshooting
Several issues commonly arise when setting up Rails 7 with React. Here are the most frequent problems and their solutions:
- CORS Issues: If your React app can't communicate with Rails, double-check the CORS configuration and ensure the origins match your development URLs exactly.
- Asset Serving Problems: Rails 7 changed how assets are handled. Make sure your build script correctly copies React build files to the Rails public directory.
- Routing Conflicts: The catch-all route for React should be the last route in your Rails routes file to avoid conflicts with API endpoints.
- Database Connection Issues: Ubuntu 24 might have different PostgreSQL configurations. Check the database.yml file and ensure the database user has proper permissions.
Here's a troubleshooting checklist for common deployment issues:
# Check Rails server status
curl -I http://localhost:3000/api/v1/posts
# Test React build
cd frontend && npm run build
ls -la build/
# Verify database connection
rails db:migrate:status
# Check for JavaScript console errors
# Open browser dev tools when accessing the app
# Test API endpoints directly
curl -X GET http://localhost:3000/api/v1/posts \
-H "Content-Type: application/json"
Performance Optimization and Best Practices
To ensure optimal performance in production, consider these configuration adjustments:
# config/environments/production.rb
Rails.application.configure do
# Enable serving of images, stylesheets, and JavaScripts from an asset server
config.asset_host = ENV['CDN_HOST'] if ENV['CDN_HOST']
# Compress CSS using a preprocessor
config.assets.css_compressor = :sass
# Enable gzip compression
config.middleware.use Rack::Deflater
# Cache React build files
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=31536000'
}
end
For the React frontend, optimize the build process:
# frontend/.env.production
GENERATE_SOURCEMAP=false
INLINE_RUNTIME_CHUNK=false
Performance comparison between different setup approaches:
Metric | Rails + React (Integrated) | Separate Deployments | Rails with Hotwire Only |
---|---|---|---|
Initial Load Time | ~800ms | ~1200ms | ~400ms |
Subsequent Navigation | ~50ms | ~100ms | ~200ms |
Build Complexity | Medium | High | Low |
Server Requirements | Single server | Multiple servers | Single server |
Real-World Use Cases and Examples
This Rails 7 + React setup works particularly well for:
- Dashboard Applications: Administrative interfaces where you need rich data visualization alongside traditional CRUD operations
- E-commerce Platforms: Product catalogs with interactive features while maintaining SEO-friendly server-rendered pages
- Content Management Systems: Editorial interfaces that benefit from React's component reusability
- API-First Applications: Services that need to support multiple frontend clients (web, mobile, third-party integrations)
A practical example might be a blog platform where Rails handles user authentication, content management, and SEO-optimized page rendering, while React manages the comment system, real-time notifications, and interactive content editor.
For production deployment on Ubuntu 24, consider using systemd services:
# /etc/systemd/system/rails-react-app.service
[Unit]
Description=Rails React Application
After=network.target
[Service]
Type=simple
User=deploy
WorkingDirectory=/var/www/rails-react-app
Environment=RAILS_ENV=production
Environment=PORT=3000
ExecStart=/home/deploy/.rbenv/shims/rails server
Restart=always
[Install]
WantedBy=multi-user.target
This setup provides a solid foundation for modern web applications that need the robustness of Rails with the interactivity of React. The key is maintaining clear separation of concerns while leveraging the strengths of both frameworks.
For more detailed information, check the official Rails Getting Started Guide and React Documentation.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.