Rails 5.2 App with Webpacker and React - Part 2

This is the second part of our series on building a Rails 5.2 application with webpack and react powered frontend, and here are some of the things we'll cover:

Overview of Part 1

  • Completely remove and bypass the asset pipeline (sprockets)
  • Exclude turbolinks
  • Use webpacker to handle stylesheets and all other assets

The Plan:

  • Add user auth using Devise
  • Structure and organize our views
  • Structure and organize JS and assets
  • Add Blueprint.js library
  • Set up public application
  • Build a simple user dashboard

Reference App:

We've created a reference app to follow along with the guide here: https://github.com/devato/rails-webpacker-react/tree/part-2

Add user auth

There are detailed instructions on how to setup devise here: https://github.com/plataformatec/devise#getting-started

Add devise to the gemfile and bundle:

gem 'devise', "~> 4.5" 

Generate devise files:

$ rails generate devise:install

Add the user model via devise:

$ rails generate devise User

Run migrations:

$ rails db:migrate

Setup public views

We're going to have two separate layouts

  1. application will be for the home page and devise views
  2. A separate app layout will be used for the dashboard.

This will allow us to use the full Rails stack with views for public pages, and then have our Dashboard a full SPA using React.

Let's start by adding some links to get around the app:

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

Add the following above the yield call:

<%= link_to 'Homepage', root_path %> 

Next, open app/views/pages/home.html.erb

And replace the contents with this snippet to start creating a functional app:

<h1>Home</h1>

<ul>
  <% if user_signed_in? %>
    <li><%= link_to('Logout', destroy_user_session_path, method: :delete) %></li>
  <% else %>
    <li><%= link_to('Login', new_user_session_path)  %></li>
    <li><%= link_to('Register', new_user_registration_path)  %></li>
  <% end %>
</ul>

This will allow us to navigate around the application but we're not concerned with the awful styling just yet.

Setup the dashboard layout

Like I said we're going to create a separate layout for the dashboard so it can be entirely handled by react.

Generate a new controller for it:

$ rails g controller app/dashboard index

Now, open the newly generated controller and set it's layout:

  layout 'app'

Create new layout app/views/layouts/app.html.erb with the following content:

<!DOCTYPE html>
<html>
  <head>
    <title>RailsWebpacker Dashboard</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= javascript_pack_tag 'app' %>
    <%= stylesheet_pack_tag 'app' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>

For clarity, application will be used for publicly accessible pages, like the homepage, and devise views. app will be used for the internal authed dashboard.

Now that our views are all ready, let's organize our JS and assets in the frontend directory.

Organize Assets

We're going to have isolate public/private assets under parent frontend directories to help with organization. Let's set up our frontend directories like this:

frontend
|-- app
|   |-- assets
|   |   |-- images
|   |   |-- styles
|   |-- components
|-- application
|   |-- assets
|   |   |-- images
|   |   |-- styles
|   |-- components
|-- packs
|   |-- app.js
|   |-- application.js

This will give us a solid structure to keep our frontend organized and easy to reason about, and will also keep assets for application separate from app.

Add blueprint.js

Blueprint.js is a collection of powerful React components. We opted to use this library to reduce the load of developing custom components.

Let's start by adding the core library using yarn:

$ yarn add @blueprintjs/core react react-dom

Stylesheets will be imported in the entrypoint, pack files for application and app

Set up public Application

Our public application is going to work differently than the private dashboard which only authed users will have access to.

Start by adding the yarn package rails-ujs which provides many features to Rails like non-GET requests, submitting form via ajax and other things.

$ yarn add rails-ujs

Let's also create a custom app/frontend/application/assets/styles/application-styles.scss to handle custom styles we'll add later.

Next, open app/frontend/packs/application.js and replace it's content with this:

import '@blueprintjs/icons/lib/css/blueprint-icons.css'
import '@blueprintjs/core/lib/css/blueprint.css'
import '../application/assets/styles/application-styles'
import Rails from 'rails-ujs'

Rails.start();

console.log('Application Pack')

This imports css from blueprint styles, imports our custom application-styles, runs Rails.start() to handle rails-ujs and logs which pack we're running.

Webpacker will automatically generate an application.css file for us based on these imports, and we just need to reference it in the application layout.

<%= stylesheet_pack_tag 'application' %>

After the page reloads, you should see the new styles take affect, Application Pack should be logged to the console and with this in place, we're done with the application section. You can register, and click around to see if everything is working as expected.

Take a break

You've earned it. Even With everything we just accomplished our app is still very basic, but is inching nearer to becoming useful.

This guide is more about putting the pieces together in a meaningful way to enable you to start from scratch in your own applications, and less about building a production app.

Whenever you're ready, let's start with the Dashboard and React SPA section of the app, and start putting it all together.

Preparation for the Dashboard

First, we need to set up our routes and controllers to restrict access to the dashboard to authed users only.

Open config/routes.rb and replace the content with this:

Rails.application.routes.draw do

  # should come before public root
  authenticated :user do
    root 'app/dashboard#index' 
  end

  root 'pages#home'
  devise_for :users
end

We have the dashboard route only for authed users, a normal root route, and devise routes.

Let's ensure only authed users have access to our Dashboard.

Open app/controllers/app/dashboard_controller.rb

And add devise's before action:

  before_action :authenticate_user!

Adding Dashboard packs

Next we need to set up our JS packs and assets in the same way we did for application.

Create app/frontend/app/assets/styles/app-styles.scss. We can leave it empty for now.

Open app/frontend/packs/app.js and replace it's content with this:

import '@blueprintjs/icons/lib/css/blueprint-icons.css'
import '@blueprintjs/core/lib/css/blueprint.css'
import '../app/assets/styles/app-styles' 
import Rails from 'rails-ujs'  

Rails.start();                 

console.log('App Pack')

Similarly to application, it imports blueprint styles etc, but the only difference is that it's logging App Pack to the console so we can be sure we've loaded the right pack.

Testing the dashboard

Now either register or login, and you should land on the Dashboard route, and see App::Dashboard#index in the view instead of home.

You should also see App Pack logged to the console.

Wrapping up

In this Part, we added some basic user features, we cleaned up our files, organized our monolith to use Rails and React interchangabely, adding a powerful React library, and are prepared to start building our Dashboard SPA. There is a lot to go over for the Dashboard, so let's continue covering what we'll need to do in the next post.

Series: