Users expect a certain level of interactivity and speed when using websites, which can be hard to provide with server rendered websites. With a regular Rails project, we can sprinkle interactivity on the client side with vanilla javascript or jQuery but it quickly becomes tedious to maintain and work with for complex user interfaces.

In this tutorial, we’re going to look at integrating React into an existing Rails 5.2 app with the react on rails gem, in order to provide an optimal user experience and keep our codebase clean at the same time.

Suppose you are running your own app store called AppGarage. Users are able to see popular apps, download them, and search for new apps.

Currently, the website is built only with Ruby on Rails so users have to type the whole search term, submit, and wait for a page refresh before seeing the search results.

Users expect content to be loaded as they type so that they can find apps faster. Wouldn’t it be nice to upgrade our search functionality to dynamically fetch and render search results as the user types their query? Let’s do that with React!

Table of Contents

Prerequisites

This tutorial assumes a basic understanding of Git, Ruby on Rails, and React/JavaScript.

Ensure the followings are installed on your device:

  • Ruby on Rails v5.2 or greater
  • Git
  • Node/NPM/Yarn

Initial Setup

Cloning

We begin by cloning the repository for our project from GitHub which includes the entire static website built with Ruby on Rails and no react integration.

Use the following command to clone the repository:

$ git clone https://github.com/danielrudn/app-garage

After cloning, enter the app-garage folder:

$ cd app-garage

Migrate and Seed the Database

Now that we pulled down the code for our project, we must prepare rails by migrating and seeding our database:

$ rails db:migrate && rails db:seed

Start Server

Our database now has the correct schema and is seeded with initial sample data in order to easily visualize our code changes. We can now start the rails server (Note: it may take rails a while to start the server on its first run):

$ rails server

You can now head over to http://localhost:3000 and you’ll see that our base application is working. We can view the homepage, search for apps, and view specific apps.

Website Screenshot

 

Now that we have a working web app, we’re ready to improve it by integrating React on Rails and modifying the search functionality.

Installing React on Rails

Note: If you’re following this tutorial using a different existing Rails app or if you’re using a Rails version older than 5.1 you should take a look at the official react-on-rails documentation for integrating with existing rails projects.

Adding and Installing Gems

First, we must add the webpacker,  react_on_rails and mini_racer gems. Edit the Gemfile and add the following to the bottom of the file:

After adding the gems to the Gemfile, install them with the following command:

$ bundle install

Setting up Webpacker and React

Now that the required gems are installed, we can begin configuration. First, we configure Webpacker by running:

$ bundle exec rails webpacker:install

Now that webpacker is configured, we install and configure React:

$ bundle exec rails webpacker:install:react

We should now see the following in our terminal:

Webpacker now supports react.js 🎉

Note: We can delete the autogenerated sample file: app/javascript/packs/hello_react.jsx

Setting up React on Rails

Currently, our project has Webpacker and supports React but we do not have an integration between React and Ruby on Rails. We need to add our changes to version control, so we add all of our changes and commit with the following command (Note: it’s important to commit our changes otherwise we will get warnings when continuing the tutorial):

$ git add . && git commit -m "Add webpacker & react"

Add the react-dom and react_on_rails packages to our package.json by running:

$ yarn add react-dom react-on-rails

Now create config/initializers/react_on_rails.rb with the following content:

We’re now ready to start writing JavaScript and React components.

Implementing the Search Component

Starting simple, we’re going to take our current search view and have it render as a React component without changing any functionality.

Create the following structure in your application folder: app/javascript/components

We can now create our search component called Search.jsx inside the folder we just created with the following content:

The above is our markup for searching converted to JSX in order for React to render it as a component. Note that we changed the HTML class and autocomplete attributes to className and autoComplete respectively for JSX to properly render our markup. This is required because we are writing JSX which is a syntax extension to JavaScript.

We now have a search component but React on Rails knows nothing about it. Whenever we create a new component that we want to use in our Rails app, we must register it with react-on-rails in order to be able to use it with the react_component rails helper. To do so, we edit the app/javascript/packs/application.js file to have the following content:

The application.js file now serves as a way for us to register our components with react-on-rails. In our case, it’s acceptable to include our search component on every page load, but for real-life production applications, it’s not very performant to include every component on every page. In real-life applications, components would be split into webpack bundles which are loaded on pages where they are needed.

Now we include our application bundle in our layout on every page by editing app/views/layouts/application.html.erbto have the following content:

Now, we’ll replace our homepage markup with the react-on-rails react_component helper to render our Search component by editing app/views/home/index.html.erb to have the following content:

Adding React Functionality to our Replacement

Our search is now rendered as a react component but all of our functionality has remained the same, the only difference is not noticeable to users yet. We’re now able to start making our search dynamic.

We need to be able to fetch our search data as JSON but we currently don’t have a JSON endpoint for our search controller. To do this, we add the file app/views/search/index.json.jbuilder with the following content:

Now our search data is accessible as JSON via /search.json.

To access our search data from the client-side JavaScript, we need to add a library for fetching data asynchronously with the browser. In this tutorial, we’ll use the axios library since it also supports older browsers. To install the library, simply run the following command in your terminal:

$ yarn add axios

Now that we have our dependencies installed, we can begin improving our search component. We must start tracking the text written into the search field, fetching the search results for the current text, and updating the state. Here is the new content for app/javascript/components/Search.jsx:

  • To start, we defined our components state (and initial state) to include our search results and whether or not we’re currently loading/fetching new results.
  • Next, we wrote our onChange function which gets called each time the value in the search field changes. We use axios to send an http request to our new /search.json endpoint with the current search field text. Axios will either successfully fetch results in which case we update our state to include the results, or it will fail and we update our state to have no results.
  • Our render function stays almost the same. We alter the input field by adding an onChange handler and pointing to the onChange function we just wrote.

The updated search component now dynamically stores and fetches the users search results based on the current text but doesn’t render anything related to the results yet.

Rendering the Dynamic Search Results

In order to render the search components state, we will create two new components that will make our code easier to manage.

First, we create the SearchResult component which is a purely functional component with no state and it renders declaratively based on props. The prop we expect is a result which is a regular app object from our rails application. Create app/javascript/components/SearchResult.jsx with the following content:

Now, we create a SearchResultList which is also a purely functional component in order to render our result array as SearchResult components. The SearchResultList will expect two props, the first is results, an array of our search results and the second is whether or not we’re currently loading new results. Create app/javascript/components/SearchResultList.jsx with the following content:

Our SearchResultList will iterate through our search results and map them to render as a SearchResult component. We added a style attribute to the container in order to properly display the results under our search field.

Now that we have our two helper components we can modify Search.jsx to render its state when the result array is not empty. Update app/javascript/components/Search.jsx with the following content:

The changes we made to the Search component were:

  • Imported our SearchResultList component
  • Updated the render function to render the SearchResultList component when we have results or when we are loading.

We’ve now integrated React on Rails into our Rails 5.2 app in order to have a dynamic search component for our users.

Conclusion

We started with a regular rails application and went through the process of installing and configuring Webpacker, React, and React on Rails. After configuring, we replaced our search to be a react component which dynamically fetches and renders search results from a new JSON endpoint on our Rails app.

Initial Application

The original implementation above wasn’t a good user experience since it involved typing the full query, waiting for the page to load before seeing any results.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Updated Implementation

The new implementation above shows search results as the user types which saves time and provides a much better user experience.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

We can now begin adding even more interactivity to our website by implementing additional react components and reusing existing components on other pages.

Preview Final Version

You can preview the final version by following these steps:

  1. Clone the final-version branch of the GitHub Repository
    $ git clone -b final-version https://github.com/danielrudn/app-garage
  2. Enter the newly cloned app-garage folder:
    $ cd app-garage
  3. Run the necessary setup commands:
    $ yarn && rails db:migrate && rails db:seed
  4. Start the rails server and navigate to http://localhost:3000 Note: Initial load may time a while.
    $ rails server

Further Reading

If you want to learn more about integrating react with Ruby on Rails (such as proper state management with Redux or handling bundles for specific pages), the repository and documentation for react on rails is a great place to look.