Rails 5.2 App with Webpacker and React - Part 1

This is the first part in a short series we're doing on building a rails app with a React enabled frontend. There are a number of ways to utilize React in a Rails 5.2 application, and after toying with different ideas and approaches we landed on using the webpacker gem to get things going with webpack as a monolith instead of having separate applications for frontend and backend.

We have created a simple demo app for reference here: https://github.com/devato/rails-webpacker-react/tree/part-1

Some notes about the setup:

  1. Completely remove and bypass the asset pipeline (sprockets)
  2. Exclude turbolinks
  3. Use webpacker to handle stylesheets and all other assets
  4. Starting a fresh app. (We may do a post on how to migrate at some point but there are already some great tutorials that cover this).

So let's get started,

Install new rails app

$ rails new rails-webpacker --webpack=react --skip-system-test --skip-coffee --skip-test --skip-sprockets --skip-turbolinks --skip-javascript

The extra flags instruct rails to skip unnecessary tools and to configure webpack to use react.

Remove assets directory

Let's remove the app/assets directory to avoid confusion with the sprockets setup.

$ rm -rf app/assets

Configure frontend

Webpacker creates an app/javascript directory as the default as it's common to keep sprockets to allow normal handling of assets using the asset pipeline but since we want webpacker to handle this, we're going to rename the javascript directory to frontend to keep things clear.

$ mv app/javascript app/frontend

Then we'll need to tell webpacker where to look for the files. Open config/webpacker.yml and edit the line:

source_path: app/javascript

Change to:

source_path: app/frontend

Create a home page

Before we generate anything using the rails command line, let's configure the app to skip generating some unnecessary files:

# config/application.rb

...
    config.generators do |g|
      g.system_tests    nil
      g.helper          false
      g.stylesheets     false
      g.javascripts     false
    end
...    

Then we can safely generate a controller:

$ rails g controller pages home

Now add the root route to config/routes.rb

root 'pages#home'

Running the server and webpack

We're going to use the trusty foreman gem to run the webpack-dev-server and the rails server. Create Procfile.dev in your rails directory:

web: bundle exec rails server
webpack: ./bin/webpack-dev-server

Then to start the server:

$ foreman start -f Procfile.dev

When any luck, you'll see that webpacker message:

webpack: Compiled successfully.

And that the rails server started successfully.

You should be able to visit the home page of your new app: localhost:5000

Adding Javascript Pack

Because rails added some default tags to the application.html.erb layout, we're going to need to change this to include our Webpacked version of the assets:

Open app/views/layouts/application.html.erb and remove any stylesheet_link_tag lines.

And add the new application js reference using: javascript_pack_tag

<%= javascript_pack_tag 'application' %>

You should see a message in the console: Hello World from Webpacker which means the the application.js file was successfully compiled using webpacker, and you're now off to the races.

Adding Stylesheets

This is where we ran into a few confusing issues, and because we were not very experienced with webpacker itself, we couldn't get stylesheets to load without breaking the JS. Instead of adding an application.scss file to packs, and then including it in the layout using stylesheet_pack_tag, we needed to import the styles into application.js and reference the imported stylesheet that is generated by webpack from the layout.

Create app/frontend/styles/app.scss and add something obvious like the default h1 to be red:

h1 {
  color: red;
}

Now we're going to import these styles globally from application.js and webpacker will handle creating a cs file matching it's own name application.

Open app/frontend/packs/application.js and add:

import '../styles/app'

Webpack will recompile the assets, but the H1 will not be red yet. This is because we need to reference webpacks application.css that was generated from the import.

Open app/views/layouts/application.html.erb and add the reference to applciation.css:

<%= stylesheet_pack_tag 'application' %>

After refreshing the page you should now see that the H1 is red.

Adding React

When creating the rails app, webpack installed react and was configured to run React code.

There was a hello_react.jsx file created in the packs directory, so we'll use this to demonstrate how to get it running, and make some changes.

Open app/views/pages/home.html.erb and add the pack tag there:

<%= javascript_pack_tag 'hello_react' %>

When the page reloads you will see "Hello React!" on the page. You can make changes to this simple component and see the changes updated in the browser.
Great so everything seems to be working.

Adding Images to React components

Because we're using webpack to handle our assets, adding an image to the hello_react can be done by importing the image into the module, then referencing it in the component.

Here is the final version of the component:

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import RailsReactImage from '../images/rails-react.png'

const Hello = props => (
  <div>
    Hello {props.name}!
    <br/>
    <img src={RailsReactImage} width="200"/>
  </div>
)

Hello.defaultProps = { 
  name: 'David'
}

Hello.propTypes = { 
  name: PropTypes.string
}

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <Hello name="React"/>,
    document.body.appendChild(document.createElement('div')),
  )
})

We imported the image from the path relative to the current file, and referenced the variable inside curly braces.

Adding Jest for Tests

Add Jest using yarn:

$ yarn add --dev jest

Now we can configure commands for running jest and tell where we keep our test files by adding those lines to package.json.

  "scripts": {
    "test": "jest",
    "test-watch": "jest --watch"
  },
  "jest": {
    "roots": [
      "spec/javascript"
    ]
  }

Add a simple test to ensure everything is working:

// spec/javascript/sum.test.js 
test('1 + 1 equals 2', () => {
  expect(1 + 1).toBe(2);
});

Run tests using yarn:

$ yarn test

Wrapping up

The webpacker gem helps tremendously when configuring your rails application to take advantage of ES6+, React and handling assets.

This is a barebones set up to help anyone who's experiementing with webpacker and React to determine if it's a viable setup. There are obviously many more things to tackle but we wanted to document how straight forward configuring Rails 5.2 to integrate React can be, and hopefully help others along the way.

Again, if you'd like a simple demo app for reference you can find it here:
https://github.com/devato/rails-webpacker-react/tree/part-1

In the next post, we're going to be added some basic features and a main App that will be handled completely by react.

Series: