Can we improve Rails user experience with React?

Jaye Hackett
4 min readApr 30, 2019

Although we should all be building progressively enhanced interfaces that work even with HTML and CSS alone, there are often substantial opportunities to improve user experience with JavaScript.

For instance:

  • Offering real-time validation of user input
  • Improving perceived performance by loading in new content without reloading an entire webpage

I’ve just spent some time using React to refactor the user interface of an existing Rails app, and here are some of the things I learned.

Why not just use jQuery?

jQuery was invented primarily to solve the problem of browsers implementing JavaScript in different ways. It was an ideal way to avoid manually writing duplicate code for each individual browser.

Nowadays, the main browsers implement JS in roughly the same way, and the problem is now one of scaling: how to deliver a rich, interactive front-end to users that is still testable, predictable and maintainable.

Given this reality, the React library is particularly worth considering because of its:

  • declarative coding style, which is often easier to understand and debug than imperative jQuery-style code
  • highly maintainable component-based paradigm, where the markup, functionality and style of an interface element all live in the same file, ripe for reuse

So, a single page app?

Many examples assume that the front-end will be cleaved off of Rails (which from then on just only JSON API endpoints) and implemented using React alone. The front-end and back-end communicate using HTTP requests and JSON data alone (i.e. everything is AJAX):

The single page app life cycle, which isn’t right for this project

This approach often makes sense for large, multi-platform apps, but is overkill for simpler web apps like mine.

In this case, I chose to use the popular react-rails gem instead, which promises:

  • gradual introduction of React components only where it makes sense, rather than outright replacement of the entire front-end
  • integration with existing Rails features like Turbolinks
  • server-rendering, which is vital for SEO and progressive enhancement

Passing data around needs forethought

Rails passes data around in a way that feels very liberal, whereas React generally requires props to be explicitly passed between components. Getting used to this difference was my first speed bump.

In pure Rails, I might set an instance variable in the controller and then use it gratuitously in all the related view and partial templates:

@collections = Collection.all

React forces you to think carefully about what data is being called on when. I ended up doing something a more complex in my controller to make sure the right data would be available for react when needed:

@collections = Collection.includes(:categories, :entries).as_json(include: { categories: {include: :entries}})

This code:

  • Makes sure that the category and entry records associated with each collection are also queried
  • And that those records are made available in the resulting JSON string with as_json

Losing path helpers

The biggest disadvantage of using react-rails is that you lose access to all the magical view helpers, particularly those for paths and static assets. You can either hard-code them or pass in the correct path to the React component as a prop.

I chose to do the latter, but this might not be maintainable:

<%= react_component "Sidebar", {  
logoUrl: asset_url("wordmark.svg"), } %>

There are third-party modules that promise to fix this problem, but I didn’t miss the path helpers enough on this project for that to be worth pursuing.

Server-rendering for free

Early single-page apps would deliver an empty HTML document with a single script tag:

<body>
<script src="bundle.js"></script>
</body>

It’s then the job of the user’s browser to render all the markup. This seemed like a good idea at the time, but has downsides:

  • Since the JavaScript bundle can be enormous, the initial page load can be slow, especially on mobile hardware or slow connections
  • Not all screen readers are compatible with this way of doing things
  • Search engines still struggle to crawl websites built this way

Nowadays, we’ve returned to the idea that rendering initial markup on the server really is crucial.

Luckily, react-rails offers a simple prerender: true option in its helpers, which makes this exceptionally easy to implement on a component basis.

It’s unclear whether this is any slower than rendering a pure Rails view template, but the developer experience is certainly simple and straightforward.

Error messages are in a different place

All error messages live in the browser’s console, unlike in vanilla Rails.

If server-rendering is turned on, you might get a generic “pre-render failed” error on the screen, which often masks the true, helpful error message.

I’ve gotten into a pattern of turning pre-rendering off while debugging, so I can be sure about whether I’m debugging the component generally or just how it pre-renders.

This is different again to how common React application frameworks like gatsby, next and create-react-app work. They throw big, prominent error messages on the browser screen.

Gradual refinement, not total reinvention

On the whole, this feels like a good approach to progressively enhanced, but still highly usable applications that have the slick UI interactions modern users, trained on Facebook and Twitter, tend to expect.

My next steps will be to delve into the performance implications (if any) and investigate any other solutions to the path helper problem.

--

--

Jaye Hackett

Strategic designer & technologist. Why use three short words when one long weird one will do? jayehackett.com