If you are reading this, you probably appreciate that Rails lets you follow some conventions and everything just works. Instead of spending time configuring development tools, you prefer to add cool features to your app. This means you probably thought Create React App was a no-brainer as soon as you heard about it. No configuring webpack or any build process anything, just use the CLI and start writing React code. Before you set off on a multi-hour journey of getting these two to work together, read this.
There are several well-documented ways of implementing React on Rails.
Another approach is to just handle the React and Rails apps as completely
separate applications. In the root of your project, do a
rails create server
create-react-app client then you can either host them separately with
Rails only being an api. In development you would start two servers, visit the
CreateReactApp on it’s port and configure it to communicate with the Rails api
back-end. In production you could host them separately or build a Docker image
that has the
yarn build output from the React app in the
/public folder of
the Rails app and the Rails app serve it from there.
This approach works. I really like the simplicity because you are on the happy-path of following all the conventions for both ecosystems.
Unfortunately the app I am building needs to have the back-end handling Authentication. We want the back-end to authenticate you if you are not signed in and hand your browser a session-cookie. This process includes redirecting the user’s browser to a 3rd party SSO provider. The above approach doesn’t work for this, because in development, your browser is hitting the Webpack Dev Server directly and Rails never gets a direct browser request I guess I could have tried to dig deeper here to see if I can get the redirect to happen or we could have considered changing the requirements and implemented all kinds of security in the front-end. But remember, this whole journey started with a desire for “Happy path, minimal configuration, Get to writing code”. So I moved on.
Here is what I spent several hours trying to get working before I quit:
rails new rails-react --skip-sprockets #this skips the asset pipeline
git add .
git commit -m 'initial rails'
#a lot more steps here, get in touch if you want to take this further
Where this is going is a Create React App application that is self contained but Rails serves it to you.
I thought it was brilliant, and I was probably correct.
Rails would start up, I would tell it to serve static assets, I would tell it that the static assets are hosted where the Webpack Dev Server is running the create-react-app code. And it would load.
The poop hit the fan right at hot reloading. The Webpack Dev Server setup has the browser open a web socket to the dev server listening for a change, and if there is a change, the browser would reload the whole page. You can view this in your Network tab of Chrome dev tools. The problem was that while Rails was serving you the JS, and I could configure Rails to go get it from the WDS, but the JS itself tried to open the socket, and it would request to open that socket to the Rails server on port 3000 instead of the WDS which I had running on port 3001. This is not configurable with the current version of CRA. I looked into several ways of messing with it, but ultimately decided against it.
In the end, I went with a minimal
webpack.config.json in the root of the app
that starts a WDS and serves a React app I am building in
reason this does work is because the WDS can be configured with a
PUBLIC_PATH which the JS code then uses to hit the WDS instead of just
using the server they were served from (which is Rails).
You will need the following in your
You will need a
This means your browser will load the JS when you try to load the Rails app. This asset URL will probably be different between Development and Production.
And lastly a normal Rails view that only contains
<div id="root"></div> and
then the React app uses that as a target to load itself into.
I hope this saves you some of the time I spent. At least if you do decide to
still have Rails serve a CRA app, you can go straight to figuring out how to
configure it’s front-end code with a custom
PUBLIC_URL. Feel free to get in
touch if you have any questions.