tirdadc

Web Development | Ruby on Rails | React.js

Migrating from react-rails to Webpacker

As an early React adopter, react-rails was the obvious choice for integrating it into Ruby on Rails, and despite countless Medium posts over the years about the need to switch to Webpack right this minute using some Rube Goldberg machine of a setup, the additional overhead and effort rarely felt warranted on existing projects. I also had little interest in fully discarding Sprockets, because it simply works incredibly well for most things.

This did present some challenges every now and then when trying to integrate external React components, as they tend to be written with the larger general JavaScript ecosystem in mind (Webpack, Browserify, etc…) and there wasn’t always a way to include them with react-rails without some rewrites.

With Rails 5.1 around the corner and the announcement that it will have native support for both Webpack and Yarn thanks to Webpacker, it finally makes sense to move things over and to also start using Webpack to manage JS dependencies without losing the benefits of Sprockets for any existing stuff.

Here’s a recap of what the steps are for this process:

Add the following gems to your Gemfile and run bundle install.

+gem 'webpacker', github: 'rails/webpacker'
+gem 'webpacker-react'

Run this Webpacker command to easily add React:

rails webpacker:install:react

In development mode, you will now need to have the Webpack watcher running in a separate process to recompile your JS/JSX on the fly.

./bin/webpack-watcher

Configure the .babelrc file if needed (I added stage 0 support):

{
  "presets": ["env", "react", "stage-0"]
}

Remove your react-rails related require statements from app/assets/javascripts/application.js and delete app/assets/javascripts/components.js. While you’re at it, clean up your config/environments files to remove the config settings for react-rails.

Move your React components to app/javascript/components/.

I didn’t feel like renaming the .js.jsx extension so I made sure that Webpack would still parse them by editing config/webpack/shared.js:

...

resolve: {
  extensions: ['.js', '.jsx', '.js.jsx', '.coffee'],
  modules: [
    path.resolve('app/javascript'),
    path.resolve('node_modules')
  ]
}

...

Rewrite your components to include proper import and export declarations:

+import React from 'react'
class TextField extends React.Component {
  // your component code
}
+export default TextField;

Keep in mind that you will have to import child components that you’re using in any given component.

If you’re using the immutability helpers anywhere, be sure to add:

+import update from 'react-addons-update'

I would recommend reading Webpacker’s README at this point to familiarize yourself with the notion of packs and how to include them in Rails. You will have to add a line to include the appropriate root component in your layout view:

<%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'vendor', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
+<%= javascript_pack_tag 'my_pack' %>

This is what my pack file looks like:

import RootComponent from 'components/test/root_component';
import WebpackerReact from 'webpacker-react';

WebpackerReact.setup({RootComponent});

Using Webpacker::React allows me to still have a react_component view helper, although there’s no support for pre-rendering yet. At some point I may just move to using the regular DOM mounting and skip this helper altogether.

Production

Install Yarn:

curl https://dl.yarnpkg.com/rpm/yarn.repo -o /etc/yum.repos.d/yarn.repo
yum install -y yarn

I also had to add this at the top of my config/webpack/shared.js:

'use strict';

Install your packages:

yarn install --production

Compile your packs (this will generate files with digests):

RAILS_ENV=production rails webpacker:compile

Update (April 29, 2017)

this was written before react-rails added support for Webpacker. You can decide to use this process to rely on Webpacker to handle everything but still keep react-rails for its react_component view helper, saving you the need to use Webpacker::React.