Look Ma, no JS! Build modern web apps using Ruby on Rails and Hotwire

In this week’s Writer’s Room blog, Andela Community member Muaad Abdirahman explains why you can put Javascript aside and build modern web apps using Ruby on Rails and Hotwire instead!

I’m sure some of you are asking yourselves the question “No JavaScript? Seriously?”. I wouldn’t blame you if you were a little skeptical. After all, there are only three things we can be certain of in this world: death, taxes, and a new JS library or framework dropping every week. I mean, your node_modules folder is already the heaviest object in the universe.

Old, recycled memes aside, JavaScript is great! We’re all in agreement there, I hope. This article simply features some new tools in the Rails community that allow us to write modern web apps using less JavaScript. Without further ado, let me introduce you to Hotwire.

What is Hotwire?

Hotwire, short for HTML Over The Wire, is a collection of tools and ideas that work together to help you build modern web applications with fast page loads by sending server-side rendered HTML over the wire (Websockets). Your Rails app can now boast the same speed and responsiveness as a Single Page App (SPA) without the added complexity of maintaining a client-side router or managing state. Just like the good old days, all your logic can now live on the server, and all the browser needs to do is render the HTML it receives.

Hotwire is made up of the following tools:

  • Turbo: This is where the magic happens, and what we’re going to focus on in this article.
  • Stimulus: A Javascript framework that takes an HTML-centric approach to state and wiring. We’re not going to cover this in this article.
  • Strada: At the time of writing this article, this has not yet been released but it promises to “standardize the way that web and native parts of a mobile hybrid application talk to each other via HTML bridge attributes.”

Getting Started

Before we get into what Turbo does, let’s discuss how we can set everything up so we can go through some code together.

If you have a Rails 7+ app, Hotwire is ready to go out of the box, so you don’t have to do anything. If you’re on Rails 6, you can install it manually by following the steps below:

  • Add the turbo-rails gem to your Gemfile
  • Run bundle install
  • Run rails turbo:install  
  • Run rails turbo:install:redis to change the adapter used by Action Cable in development to Redis. The Async adapter, which is the default, does not support Turbo Stream broadcasting. Check out Redis.

Turbo

Turbo is what lets us realize the dream of creating fast web apps without writing any JavaScript ourselves. This is achieved by using the following tools:

Turbo Drive

Turbo Drive is a successor to Turbolinks which, for a number of years now, made navigating around Rails apps faster. This was achieved by Turbolinks intercepting clicks on all qualifying links ( <a href> links to the same domain), automatically fetching the requested page in the background and replacing the current page’s <body> element with that in the response from the server. The page would then get updated without doing a full reload. That functionality still lives on in Turbo Drive. In addition to that, Turbo Drive now supports submitting forms asynchronously and processing the response in much the same way it does for link clicks. Now, you don’t have to add data-remote=true to the form anymore as we used to do with Rails UJS. It just works.

Turbo Frames
Turbo Frames helps you decompose a page into multiple components (turbo frames) so that any link clicks and form submissions within that frame replace the contents of the frame with the response from the server. Rails makes these requests in the background and provides updates to the page without doing a full reload. This may sound a bit like Turbo Drive but the difference is that the updates happen within a frame and the rest of the page stays the same. Let us look at the example below.

In the demo above, the “Add A Post” link is in a turbo frame with an ID of post_form and links to the /posts/new path.

The HTML in the posts/new.html.erb file is wrapped in a turbo frame whose ID is also post_form

When you click on the “Add A Post” link, Rails makes a request to /posts/new and, in the response, finds a turbo frame with the same ID as the turbo frame that contains the link. It extracts the HTML in the turbo frame from the response and replaces the contents of the frame. This happens without leaving the page.

The “Back” link goes to /posts so, clicking it will break out of the post_form turbo frame if the response from the server does not include a turbo frame with the same ID.

Let’s look at one more example to help us demonstrate how turbo frames work. In the example below, we have a list of posts. You can click on the “Edit” link to edit a post. Normally, this would open the edit form within a new page but, in this case, the edit form is rendered inline and replaces the post itself. Clicking “Submit” or “Cancel” will dismiss the edit form and bring back the post. The rest of the page stays the same.

The HTML to display a specific post is enclosed in a turbo frame with the ID computed by dom_id(post) . This translates to post_#{post.id} where post.id is the ID of the post in the database. For this example, let us assume the post ID is 6. The ID of the turbo frame becomes post_6 .

When we click on the “Edit” link, Rails makes a request for /posts/6/edit and responds with posts/edit.html.erb which looks like this:

Rails will find a turbo frame with an ID of post_6 . It will grab this frame from the HTML response and replace everything in the frame where the “Edit” link was.

I hope that was clear enough. If not, you can take a look at this Github repo and see this in action.

Let’s say someone accesses your web app with JavaScript disabled. Will this still work? The answer is yes. Clicking on a link in a turbo frame will open the requested resource on a new page if JavaScript is disabled. Talk about graceful degradation. Try that out!

Eager-load Parts Of The Page With Turbo Frames

Sometimes, we may want to load certain sections of a page separately in order to improve page load speeds. In the example below, we have a section of the page that lists out all the posts. We also have a post count in the navbar. In this example, we won’t see any noticeable change in performance but you can imagine how it will work for a more complicated app.

We could get all the posts and posts count in one request but that could take some time and may even slow down your site a bit. With Turbo Frames, you can first render just the important parts of the page – say the posts list – and then make one more request to fetch the posts count. And, no, we don’t need to write any JavaScript. All we have to do is add the posts count component in a turbo frame and pass a URL to fetch the posts count to its src attribute.

The “ posts-count ” turbo frame will have a loading spinner as we wait for /posts/count to return a response. This request in our case is handled by the posts_count action in posts_controller .

The posts_count action will respond back with the posts/_posts_count.html.erb partial which looks like this:

The key thing to note here is that the turbo frame where the posts count is going to be rendered and the one contained in the posts/_posts_count.html.erb partial have the same ID. Once the request to /posts/count is successful, the spinner will get replaced by the posts count.

As you can see in the demo below, the posts list gets rendered on the first load, while the posts count in the navbar gets loaded separately. If you’re wondering why loading the posts count is taking that long, I added a sleep in the action to demonstrate the fact that we are loading something so we can see the spinner in action.

I think that just about does it for Turbo Frames. It can take some time to wrap your head around it but it is simple to implement.

Turbo Streams

And, to round out the holy trinity of tools in our Turbo toolbox, we will dedicate a separate upcoming article to Turbo Streams. Just to whet your appetite a little bit, it allows you to deliver partial updates to your pages asynchronously via WebSockets. Watch this space!

Writer’s Room

Andela’s Community of technologists features talented specialists who are experts in their respective fields. To celebrate their talent, we’re created the Writer’s Room program, where we offer writing workshops to our community and encourage them to write blogs and tutorials around their areas of expertise.

Want to be part of the Andela Community? Then join the Andela Talent Network!

With more than 175,000 technologists in our community, in over 90 countries, we’re committed to creating diverse remote engineering teams with the world’s top talent. And our network members enjoy being part of a talented community, through activities, benefits, collaboration, and virtual and in-person meetups.

All you need to do to join the Andela Talent Network is to follow our simple sign-up process. 

Submit your details via our online application then…

Complete an English fluency test – 15 minutes.

Complete a technical assessment on your chosen skill (Python, Golang, etc.) – 1 hour.

Meet with one of our Senior Developers for a technical interview – 1 hour.


Visit the Andela Talent Network sign-up page to find out more.

If you found this blog useful, check out our other blog posts for more essential insights!

Related Posts