Rails 5.1 loves Javascript

Ruby/Rails developers have had a controversial relationship with Javascript for the past years. True or false, Javascript was accused of being the holdback for web; we all know that this is not true anymore. The language has improved significantly over the past few years; partly due to advent of ES6.

Furthermore, the rivalry between Rails and Javascript doesn’t exist anymore. If you want to create ambitious web applications the only option is Javascript! Basically, there is no other choice.

This makes it possible for the two ecosystems to thrive next to each other. Javascript is great for frontend work; also it could be an option for backend implementation; however some people prefer to write their backend in other languages. For that, Ruby/Rails could be a great choice.

Apparently that hate relationship is improving now with onset of Rails 5.1. The first beta version of 5.1 is out and it comes with out of the box support for Webpack/React/Angular/Vue.js. Surprise!

Rails 5.1 is a huge step in the right direction. It includes webpacker gem which lets us to use Webpack to manage app-like JavaScript modules in Rails.

Webpacker makes it easy to use the JavaScript preprocessor and bundler Webpack to manage application-like JavaScript in Rails.

The nice thing about webpacker is that it coexists with asset pipeline, as the purpose is to use it for app-like Javascript (served from app/javascript/packs directory), not images, css or even Javascript snippets. These will continue to be served from app/assets directory. More details on this later.


Let’s see how we can harness the power of Rails 5.1 to create a Rails/React app. Note that Rails 5.1 is a beta version; so let’s not rely on it for production.

First thing to do is to install Rails 5.1. Let’s create a Gemfile with the following content:

source 'https://rubygems.org'
ruby '2.2.5'
gem 'rails', github: 'rails/rails'

This makes sure that we are pulling the most recent version of rails from master branch on github (at this moment it’s Rails 5.1.0beta1).

Then we just need to run bundle install to install Rails.

Voila!

Now that we have Rails 5.1 installed, we can run the following to create a React friendly Rails app:

rails new myapp --webpack=react

When this command succeeds we have a new folder named myapp in the current directory. We need to cd into it and run bundle install to install all dependencies. The next necessary step is to install javascript dependencies via yarn or npm. If we settle on yarn for this, we just need to run yarn, this will install our Javascript dependencies.

Note: to use webpacker with angular and Vue create your new app as following:

rails new myapp --webpack=angular #webpacker with angular
rails new myapp --webpack=vue #webpacker with Vue.js

At this point, Rails has created a javascript friendly app for us. If you look under app/javascript you see a new folder named packs with two files in it (application.js and hello_react.js). This packs folder is a new thing added in Rails 5.1.

app/javascript
└── packs
├── application.js
└── hello_react.js
1 directory, 2 files

Everything under packs directory is automatically compiled by Webpack. The best practice is to place actual application logic in a relevant structure within app/javascript and only use these pack files to reference that code so it’ll be compiled. Let’s have a look at hello_react.js:

import React from 'react'
import ReactDOM from 'react-dom'
class Hello extends React.Component {
render() {
return <div>Hello {this.props.name}!</div>
}
}
document.addEventListener("DOMContentLoaded", e => {
ReactDOM.render(<Hello name="React" />, document.body.appendChild(document.createElement('div')))
})

It’s a react component! Rails has generated us a sample react component that we can render in a view whenever we want. We can even write jsx in it. What could be better!?

This react component only renders Hello React but we can make complex components if we need to.

The next step is to render our component using Rails. Let’s create a Rails controller:

bundle exec rails g controller Pages index

This command created a PagesController with an index method in it. It also creates us a view template at app/views/pages/index.html.erb. To render our React component, we just need to replace the content of the file with:

<%= javascript_pack_tag 'hello_react' %>

This tells Rails to embed our React component in this view.

We have everything ready now. We just need to run our rails server in one terminal and run our webpack-dev-server in another one; but before that let’s add a small tweak to our config/environments/development.rb to enable javascript_pack_tag to load assets from webpack-dev-server:

config.x.webpacker[:dev_server_host] = "http://127.0.0.1:8080"

We are ready to run our servers now. In one terminal, run bundle exec rails server to start Rails server and in the other one run:

./bin/webpack-dev-server --host 127.0.0.1

If you open your browser and navigate to http://localhost:3000/pages you should see Hello React on your screen.

Note: you may face No data received ERR_EMPTY_RESPONSE error when running your server. Have a look at this issue for solutions.


Dissecting Webpacker

According to the docs:

Webpacker ships with three binstubs: ./bin/webpack./bin/webpack-watcher and ./bin/webpack-dev-server. They're thin wrappers around the standard webpack.js executable, just to ensure that the right configuration file is loaded.

./bin/webpack-dev-server will launch a Webpack Dev Server listening on http://localhost:8080/ serving our pack files. It will recompile the files as we make changes. We setconfig.x.webpacker[:dev_server_host] in ourdevelopment.rb to tell Webpacker to load our packs from the Webpack Dev Server. This setup allows us to leverage advanced Webpack features, such as Hot Module Replacement. If we want to enable Hot Module Loading we just need to send a --hot option to webpack-dev-server binstub when running it:

./bin/webpack-dev-server --hot --host 127.0.0.1

Webpacker provides us with a default set of configuration for development and production environments. If you look under config/webpack you see:

config/webpack
├── development.js
├── production.js
└── shared.js
0 directories, 3 files

Feel free to change them according to your needs but in most basic cases the default configs are good enough.

Linking to Sprockets’ assets

Lots of times we need to load normal assets that are served using asset pipeline. To do so, we just need to add .erb extension to our javascript file, then we can use Sprockets’ asset helpers to load the asset:

// app/javascript/my_pack/example.js.erb

<% helpers = ActionController::Base.helpers %>
var catImagePath = "<%= helpers.image_path('kitten.png') %>";

According to the docs, this has been enabled by the rails-erb-loader loader rule in config/webpack/shared.js.

Deploying to Heroku

The first step to deploy our app on Heroku is to add a Procfile with following content:

web: bundle exec puma -p $PORT

After that we just need to create a new heroku app and add respective buildpacks for node and ruby:

heroku create 
heroku buildpacks:add --index 1 heroku/nodejs
heroku buildpacks:add --index 2 heroku/ruby

After that we can push our app to heroku with:

git push heroku master

Done!

You can find the full source code for this example here.

Resources:

  1. Rails 5.1.0beta1 announcement
  2. Webpacker gem homepage
  3. Webpack Dev Server
  4. Using multiple buildpacks for an app on heroku
  5. Source code for the created sample app in this post