Setting up react-rails

There are several libraries that integrate React directly with the Rails asset pipeline. When I started, we were using a couple of different ones, but I decided to rely solely on react-rails.

react-rails ships with a couple of different utilities, chief among them being webpacker and react_ujs. It uses the former to build a webpack configuration object dependent upon your app’s configured environment, and it uses the latter to render React components inside of Rails templates.

At the time of writing, react-rails shipped with v3 of the webpacker library. In order for us to use some newer features of webpack, we’ll want to upgrade to v4. Throughout the rest of this article, we’ll be working with webpacker@4.0.0.rc.2.

There are three sections of our configuration that we’ll be interested in:

config/webpacker.yml — Webpacker configuration config/webpack/environment.js — Webpack configuration object .babelrc — Babel configuration

It can be easy to confuse webpacker and webpack. Just remember that webpacker is the Ruby library and webpack is the Javascript compiler.

For your package.json file, replace your dependencies and resolutions with the following, and then run npm install or yarn install :

Follow the instructions provided in the react-rails documentation, and then, before opening your webpacker.yml file, make sure to include the following lines in your gemfile and run bundle install :

gem 'webpacker', '~> 4.0.0.rc.2'

gem 'react-rails'

webpacker.yml

This file contains configurations for your app’s different environments. We’re really only concerned with the top section, default , as it’s passed to the rest of the environments.

This is what you should see when you first open the file (I’ve removed the comments):

default: &default

source_path: app/javascript

source_entry_path: packs

public_output_path: packs

cache_path: tmp/cache/webpacker check_yarn_integrity: false

webpack_compile_output: false resolved_paths: [] cache_manifest: false extract_css: false extensions:

- .mjs

- .js

- .sass

- .scss

- .css

- .module.sass

- .module.scss

- .module.css

- .png

- .svg

- .gif

- .jpeg

- .jpg

Here are the important bits:

source_path and source_entry_path are concatenated together with a glob pattern (built using the extensions attribute) in order to build bundles for each file in your project.

and are concatenated together with a glob pattern (built using the attribute) in order to build bundles for each file in your project. Your bundles are compiled to public_output_path , and cached to cache_path .

, and cached to . Webpack will lookup additional modules listed in resolved_paths when compiling your bundles.

We’ll be storing our frontend code in app/react , and we will have a simple Javascript file that utilizes the react_ujs module to create an entrypoint for our router component.

We’re also going to be using JSX within React, so we want to make sure we include the .jsx file extension in our configuration.

With that being said, replace the contents of your config/webpacker.yml file with the following:

default: &default

source_path: app/react

source_entry_path: entrypoints

public_output_path: packs

cache_path: tmp/cache/webpacker check_yarn_integrity: false

webpack_compile_output: false resolved_paths: [] cache_manifest: false extract_css: false extensions:

- .jsx

- .js

- .sass

- .scss

- .css

- .module.sass

- .module.scss

- .module.css

- .png

- .svg

- .gif

- .jpeg

- .jpg

environment.js

Webpacker uses this Javascript file to import a custom class instance, environment , which is ultimately used to export a configuration object for webpack.

We can use this file to customize the output that is used by webpack in order for us to implement various plugins and loaders. You can read more about this file and its options in the documentation.

In order to make the router system work properly, we want to use the split chunks plugin. We’re also going to install a plugin to help us in creating a manifest file. Finally, we want to include the babel-loader plugin so that we can use ES6 and beyond.

This configuration will ultimately tell webpack to run our code through Babel, and to create a single asset bundle for all of our node_modules that we can then include in our default Rails template.

In your config/webpack/environment.js file, replace any existing code with the following:

const {environment} = require(' @rails/webpacker ') const WebpackAssetsManifest = require('webpack-assets-manifest') environment.config.merge({

optimization: {

splitChunks: {

cacheGroups: {

vendor: {

test: /[\\/]node_modules[\\/]/,

name: 'vendor',

chunks: 'all'

}

}

}

}

}) environment.plugins.insert(

'Manifest',

new WebpackAssetsManifest({

entrypoints: true,

writeToDisk: true,

publicPath: true

})

) environment.loaders.prepend(

'jsx',

{

test: /\.(jsx|js)x?$/,

exclude: /node_modules/,

loader: 'babel-loader'

}

) module.exports = environment

.babelrc

A very simple file containing a single JSON object with our Babel configuration. We will need to use Babel in order for our router to dynamically import the components that React lazy loads for us.

Create a .babelrc in the root directory of your project and add this code to it:

{

"presets": [

"@babel/preset-env",

"@babel/preset-react"

],

"plugins": [

"@babel/plugin-syntax-dynamic-import",

"@babel/plugin-proposal-object-rest-spread",

[

"@babel/plugin-proposal-class-properties",

{

"spec": true

}

]

]

}

Babel v7 introduces another root file that can be used in addition to our .babelrc file.

So, we’ll want to create babel.config.js in the root of our project with the following code: