I love Rails! I also really like React for complex, interactive components. I’ve found it very product to use Turbo and Stimulus (from Hotwire) with sprinkles of React.
With Rails 7, esbuild is now the default javascript tooling. In the past, I’ve used react-rails
but their documentation is still aimed at webpacker
based Rails projects. Thankfully, a kind soul found a working setup with esbuild. I took their working example repo and made this step by step guide.
Dependencies
Add gem "react-rails"
to your Gemfile
.
Add the following to your package.json
.
"esbuild-plugin-import-glob": "^0.1.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react_ujs": "^2.6.1",
Install these using your preferred command line tools.
esbuild
Add the following to your esbuild.config.js
. Below the existing require
statements at the top of the file:
const ImportGlobPlugin = require('esbuild-plugin-import-glob').default;
Next, add ImportGlobPlugin()
to the plugins
array in your config
object. It should look like this after plugins: [rails(), ImportGlobPlugin()],
.
Next, add loader: { '.js': 'jsx' },
to your config
object.
Components
Create the directory app/javascript/components
.
Create a app/javascript/components/index.js
file with the following contents:
import components from "./**/*.jsx"
let componentsContext = {}
components.forEach((component) => {
componentsContext[component.name.replace(".jsx", "").replace(/--/g, "/")] = component.module.default
})
const ReactRailsUJS = require("react_ujs")
ReactRailsUJS.getConstructor = (name) => {
return componentsContext[name]
}
ReactRailsUJS.handleEvent('turbo:load', ReactRailsUJS.handleMount, false);
ReactRailsUJS.handleEvent('turbo:frame-load', ReactRailsUJS.handleMount, false);
ReactRailsUJS.handleEvent('turbo:before-render', ReactRailsUJS.handleUnmount, false);
Add import "./components"
to application.js
.
Create a component to render. Here’s a simple HelloWorld.jsx
component you can use:
import React from 'react';
export default function HelloWorld() {
return <h1>Hello World</h1>;
}
Voila, you can use your component.
react_component "HelloWorld"