Setting Up React and Bootstrap 4 on Rails 6

Sun Sep 8, 2019
~800 Words

In this post we will take a freshly-created Rails 6 app and set up React with Bootstrap 4 such that both the Rails and React-based views can use Bootstrap components.

Setting up Bootstrap 4

Install the necessary dependencies

yarn add bootstrap jquery popper.js

Update config/webpack/environment.js

Here we add configuration for ProvidePlugin so that jQuery and popper.js are available in all views:

const { environment } = require('@rails/webpacker')

const webpack = require('webpack')

environment.plugins.prepend('Provide', new webpack.ProvidePlugin({
    $: 'jquery',
    JQuery: 'jquery',
    Popper: ['popper.js', 'default'], // for Bootstrap 4
  })
)

module.exports = environment

Add import for bootstrap to app/javascript/stylesheets/application.scss

First, rename the stylesheet if you haven’t done so already:

mv app/javascript/stylesheets/application.css app/javascript/stylesheets/application.scss

Then add the import statement:

/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
 * vendor/assets/stylesheets directory can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
 * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
 * files in this directory. Styles in this file should be added after the last require_* statement.
 * It is generally better to create a new file per style scope.
 *
 *= require_tree .
 *= require_self
 */

@import "bootstrap/scss/bootstrap";

Add your first page

Assuming you are still seeing the Rails welcome view, we need to set up the first view, e.g.:

config/routes.rb:

Rails.application.routes.draw do
  root 'pages#main'
end

app/controller/pages_controller.rb:

class PagesController < ApplicationController
  def main
  end
end

Then add an empty template file:

mkdir app/views/pages
touch app/views/pages/main.html.erb

On loading this view if you put some placeholder content into the main.html.erb template then you should see it rendered with Bootstrap’s default styling.

Some example placeholder content to exercise Bootstrap:

<div class="alert alert-primary" role="alert">
  A simple primary alert to test Bootstrap with.
</div>

Update the layout

Finally let’s set up the layout to include the necessary Bootstrap meta tags, and a container for the view contents:

In app/views/layouts/application.html.erb:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Hello, world!</title>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all' %>
    <%= javascript_pack_tag 'application' %>
  </head>
  <body>
    <div class="container">
      <%= yield %>
    </div>
  </body>
</html>

React Setup

Run the built-in installer:

bin/rails webpacker:install:react

This will install the necessary dependencies and update your babel and webpacker configurations to transpile JSX for you, amongst other things.

It creates an example pack (entry point) using React which you can use to test the integration.

The example pack lives at app/javascript/packs/hello_react.jsx, so in your root view (in our case app/views/pages/main.html.erb) you can add the following to load it:

<%= javascript_pack_tag 'hello_react' %>

For reference the code for hello_react.jsx is:

// Run this example by adding <%= javascript_pack_tag 'hello_react' %> to the head of your layout file,
// like app/views/layouts/application.html.erb. All it does is render <div>Hello React</div> at the bottom
// of the page.

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

const Hello = props => (
  <div>Hello {props.name}!</div>
)

Hello.defaultProps = {
  name: 'David'
}

Hello.propTypes = {
  name: PropTypes.string
}

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <Hello name="React" />,
    document.body.appendChild(document.createElement('div')),
  )
})

What it’s doing is setting up a basic React component, and then rendering it into a div that it’s appending to the end of the page content. This way there’s no need to have a particular element available in the page to render into.

At this point you would want to set up your own entry point / pack for your application or view.

As we are going to load this pack via the pages#main for this simple app, we will rename the entrypoint appropriately:

mv app/javascript/packs/hello_react.jsx app/javascript/packs/main.jsx

Loading the Rails app now gives us ‘Hello React!’ rendered using Bootstrap’s basic styling.

Settting up react-bootstrap

Install react-bootstrap:

yarn add react-bootstrap

We can now test the integration by including a react-bootstrap component in our view, in this case a button:

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import Button from 'react-bootstrap/Button';

const Hello = props => (
  <Button>Hello {props.name}!</Button>
)

Hello.defaultProps = {
  name: 'David'
}

Hello.propTypes = {
  name: PropTypes.string
}

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <Hello name="React" />,
    document.getElementById('application'),
  )
})

On reloading the page you should see a Bootstrap-styled button.

Wrapping Up

At this point you should now have a working Rails 6 app with:

  • Bootstrap 4 styles loaded
  • A JS entry point which loads the beginnings of your JS app/view
  • Access to all react-bootstrap components