.   .   .
We're always looking for great talent‼️ πŸš€πŸ˜„ And we're passionate about driving innovation and improving lives. Join us as we help social impact organizations and enterprise customers build their web apps. Apply today!
.   .   .

ReactJs is awesome and Ruby on Rails is awesome. Yes, I get that Ruby on Rails is a resource hog and there are much better choices for serving a ReactJs app on that front… but Rails development is fast and when my developers work on Rails projects, they’re productive. So we wanted to throw ReactJS on top of Rails and had the following objectives.

Objectives

  1. We wanted to use ReactJS and react-router to serve the majority of the routes for the application.
  2. We wanted to be able to still use Rails to handle certain routes that the ReactJS app just didn’t need to know anything about.
  3. We didn’t want to use the react-rails gem (which is awesome by the way and allows server side rendering).
  4. We wanted to build a modular and isolated ReactJs app that could easily be switched to a different backend with close to no code modification in the future.
  5. We didn’t want to do anything the Rails way (or in this case, the react-rails way). We wanted to write ReactJs code the ReactJS way.

After some playing around, we ended up with a structure and workflow that we’re pretty happy with. You can see the end result here (old version that matches this post). Below, I will explain the steps we took to modify our Rails setup to accommodate ReactJS. Note: The ReactJS app in this example is extremely simple as the purpose of this post is not to teach you how to do ReactJS development. When done, your project should look something like this (the bolded are the ones you should take particular note of):

root
β€” app
β€” bin
β€” config
β€” db
β€” Gemfile
β€” Gemfile.lock
β€” gulpfile.js
β€” lib
β€” log
β€” node_modules
β€” package.json
β€” public
β€” Rakefile
β€” tmp
β€” react
β€”- app.js
β€”- components
β€”β€” RailsStarterApp.js
β€”β€” layouts
——– Base.js
β€”β€” pages
——– BusPage.js
——– CarPage.js
——– HomePage.js
——– NotFoundPage.js
β€”β€” vehicles
——– Bus.js
——– Car.js
——– Wheel.js

Now it’s time to get started.

Create your Gulpfile.js and Package.json files

You will need to have nodejs and npm installed. Also, install Gulp globally.

// {RAILS_ROOT}/gulpfile.js
'use strict';
 
// Credits to https://github.com/gulpjs/gulp/blob/master/docs/recipes/fast-browserify-builds-with-watchify.md
 
var watchify   = require('watchify');
var browserify = require('browserify');
var gulp       = require('gulp');
var source     = require('vinyl-source-stream');
var buffer     = require('vinyl-buffer');
var gutil      = require('gulp-util');
var sourcemaps = require('gulp-sourcemaps');
var assign     = require('lodash.assign');
var reactify   = require('reactify');
var babelify   = require("babelify");
 
// add custom browserify options here
var customOpts = {
  entries: ['./react/app.js'],
  transform: [reactify, babelify], // We want to convert JSX to normal javascript and es6 to es5
  debug: true
};
var opts = assign({}, watchify.args, customOpts);
var b = watchify(browserify(opts));
 
// add transformations here
// i.e. b.transform(coffeeify);
 
gulp.task('js', bundle); // so you can run `gulp js` to build the file
b.on('update', bundle); // on any dep update, runs the bundler
b.on('log', gutil.log); // output build logs to terminal
 
function bundle() {
  return b.bundle()
    // log errors if they happen
    .on('error', gutil.log.bind(gutil, 'Browserify Error'))
    .pipe(source('react-bundle.js'))
    // optional, remove if you don't need to buffer file contents
    .pipe(buffer())
    // optional, remove if you dont want sourcemaps
    .pipe(sourcemaps.init({loadMaps: true})) // loads map from browserify file
       // Add transformation tasks to the pipeline here.
    .pipe(sourcemaps.write('./')) // writes .map file
    .pipe(gulp.dest('./app/assets/javascripts'));
}
 
gulp.task('default', ['js']);
- See more at: https://labs.chiedo.com/creating-a-basic-reactjs-app-with-rails-serving-the-api-without-react-rails/#sthash.DcTIJusz.dpuf

Now setup your Gulpfile.js Next setup your package.json.

// {RAILS_ROOT}/package.json
{
  "name": "rails-starter",
  "version": "0.0.0",
  "description": "",
  "main": "gulpfile.js",
  "dependencies": {
    "envify": "~1.2.1",
    "es6-promise": "~1.0.0",
    "react": "~0.13.1",
    "reactify": "~1.1.0",
    "react-router": "~0.13.3"
  },
  "devDependencies": {
    "envify": "~1.2.1",
    "gulp": "~3.8.0",
    "gulp-concat": "~2.2.0",
    "reactify": "~1.1.0",
    "gulp-uglify": "~0.3.1",
    "jest-cli": "~0.4.5",
    "vinyl-source-stream": "~1.1.0",
    "watchify": "~2.2.1",
    "browserify": "~10.2.1",
    "babelify": "~6.1.2",
    "vinyl-buffer": "~1.0.0",
    "gulp-util": "~3.0.4",
    "gulp-sourcemaps": "~1.5.2",
    "lodash.assign": "~3.2.0"
  },
  "author": "Chiedo John",
  "license": "Private"
}
- See more at: https://labs.chiedo.com/creating-a-basic-reactjs-app-with-rails-serving-the-api-without-react-rails/#sthash.DcTIJusz.dpuf

Create a rails controller and view for serving your app and remove traces of turbolinks.

# {RAILS_ROOT}/app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
  def index
    render "index"
  end
end
# {RAILS_ROOT}/app/models/static_page.rb
class StaticPage < ActiveRecord::Base
end
<!-- {RAILS_ROOT}/app/views/static_pages/index.html.erb -->
 
<% content_for :footer_scripts do %>
  <%= javascript_include_tag "react-bundle" %>
<% end %>
<div id="react-app"></div>
<!-- {RAILS_ROOT}/app/views/layouts/application.html.erb -->
 
<!DOCTYPE html>
<html>
<head>
  <title>ExampleApp</title>
  <%= stylesheet_link_tag    'application', media: 'all' %>
  <%= javascript_include_tag 'application' %>
  <%= yield :header_scripts %>
 
  <%= csrf_meta_tags %>
</head>
<body>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
 
 
<%= yield %>
 
<%= yield :footer_scripts %>
</body>
</html>
//{RAILS_ROOT}/app/assets/javascripts/application.js
 
//= require jquery
//= require jquery_ujs

Connect your React app

As long as your React app compiles to app/assets/javascripts/react-bundle.js and mounts to an element with the ID of ‘react-app’, you are fine, but if you want an example of code and want to use the Gulp setup that we went with, you will want to copy all of the files here to a folder by the name of ‘react’ in your rails root directory. Then you will need to modify a couple more rails files.

# {RAILS_ROOT}/config/routes.rb
Rails.application.routes.draw do
  # Root URL of the app
  root to: "static_pages#index"
  scope :api do
    # This is where your API from rails would live
  end
 
  # Redirect everything not explicitly defined to reactjs
  get "*path" => "static_pages#index"
end
# {RAILS_ROOT}/config/initializers/assets.rb
 
Rails.application.config.assets.version = '1.0'
Rails.application.config.assets.precompile += %w( react-bundle.js )

Now you’re ready to try it out.

You will need to run ‘npm install’ from your rails root to install all the needed node modules. Then you will need to run ‘gulp’ in a terminal window from your rails root to compile your ReactJS app and move it to the correct directory in the rails folder. Once you have done that, you should be able to run your rails server and everything should work like a charm. If you used my basic React app as listed above, you will be able to use the paths of ‘/car’ and ‘/bus’ in your browser and see different pages that are served by React.

If you have any issues, be sure to check the sample code at https://github.com/chiedo/rails-starter/tree/react/.

Feel free to let me know if you have any issues or any questions. If you need help integrating React with your Rails project, feel free to email us at labs@chiedo.com.

.   .   .

We're Hiring‼️ ?? Looking to join our team of web developers? We're passionate about innovation, family, and community. Apply today!