# React Integration These steps describe how to create a Rails/React app, using Shakapacker as the bundler. Before starting, ensure that you have Yarn installed, for example: ```shell npm i -g yarn ``` ## Basic Setup Create a new Rails app as per the [installation instructions in the README](https://github.com/shakacode/shakapacker#installation). Add React, as well as the necessary libraries to enable CSS support in your application: ```shell yarn add react react-dom @babel/preset-react yarn add css-loader style-loader mini-css-extract-plugin css-minimizer-webpack-plugin ``` Update the Babel configuration in the `package.json` file: ```diff "babel": { "presets": [ "./node_modules/shakapacker/package/babel/preset.js", + "@babel/preset-react" ] }, ``` And that's it. You can now create a React app using `app/javascript/application.js` as your entry point. ## Enabling Hot Module Replacement (HMR) With HMR enabled, Shakapacker will automatically update only that part of the page that changed when it detects changes in your project files. This has the nice advantage of preserving your app’s state. To enable HMR in a React app, proceed as follows:. In `config/webpacker.yml` set `hmr` is set to `true`. Install the [react-refresh](https://www.npmjs.com/package/react-refresh) package, as well as [@pmmmwh/react-refresh-webpack-plugin](https://www.npmjs.com/package/@pmmmwh/react-refresh-webpack-plugin): ```shell yarn add --dev react-refresh @pmmmwh/react-refresh-webpack-plugin ``` Alter `config/webpack/webpack.config.js` like so: ```js const { webpackConfig, inliningCss } = require('shakapacker'); const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); const isDevelopment = process.env.NODE_ENV !== 'production'; if (isDevelopment && inliningCss) { webpackConfig.plugins.push( new ReactRefreshWebpackPlugin({ overlay: { sockPort: webpackConfig.devServer.port, }, }) ); } module.exports = webpackConfig; ``` This applies the plugin to the webpack configuration. Delete the Babel configuration from `package.json`: ```diff - "babel": { - "presets": [ - "./node_modules/shakapacker/package/babel/preset.js", - "@babel/preset-react" - ] - }, ``` Then create a `babel.config.js` file in the root of project and add the following: ```js module.exports = function (api) { const defaultConfigFunc = require('shakapacker/package/babel/preset.js') const resultConfig = defaultConfigFunc(api) const isDevelopmentEnv = api.env('development') const isProductionEnv = api.env('production') const isTestEnv = api.env('test') const changesOnDefault = { presets: [ [ '@babel/preset-react', { development: isDevelopmentEnv || isTestEnv, useBuiltIns: true } ] ].filter(Boolean), plugins: [ isProductionEnv && ['babel-plugin-transform-react-remove-prop-types', { removeImport: true } ], process.env.WEBPACK_SERVE && 'react-refresh/babel' ].filter(Boolean), } resultConfig.presets = [...resultConfig.presets, ...changesOnDefault.presets] resultConfig.plugins = [...resultConfig.plugins, ...changesOnDefault.plugins ] return resultConfig } ``` This is taken from the [sample React Babel config](https://github.com/jameshibbard/shakapacker/blob/master/docs/customizing_babel_config.md#react-configuration). HMR for your React app is now enabled. 🚀 ## A Basic Demo App To test that all of the above is working, you can follow these instructions to create a basic React app using Shakapacker. 1. Create a new Rails app: ```shell rails new myapp --skip-javascript cd myapp bundle add shakapacker --strict ./bin/bundle install ./bin/rails webpacker:install yarn add react react-dom @babel/preset-react yarn add css-loader style-loader mini-css-extract-plugin css-minimizer-webpack-plugin ``` 2. Generate a controller ```shell rails g controller site index echo '
' > app/views/site/index.html.erb ``` 3. Create a CSS file and a React component: ```shell touch app/javascript/App.css app/javascript/App.js ``` 4. Edit `app/javascript/application.js` like so: ```jsx import React from 'react'; import { createRoot } from 'react-dom/client'; import HelloMessage from './App'; const container = document.getElementById('root'); const root = createRoot(container); document.addEventListener('DOMContentLoaded', () => { root.render(