Hotwire truly is a game changer for Ruby on Rails web application development. Here's why.
Photo by Erik Mcleanon on Unsplash
Once upon a time, I could just write code in Ruby and Rails. Life was good. Then the tyranny of the Single Page Apps (SPA) or Progressive Web Apps (PWA) began, and I was forced to incorporate JavaScript frameworks. Now, I was dealing with two separate worlds. The problem was I didn’t want to leave my Ruby world. I was quite happy there.
More skill sets were needed on the team. Where once we had one technology to mostly rule them all, now there were two. In practical terms, it was more than two. The JavaScript framework of the month changed over time. I remember the first time I used AngularJS. It was overwhelming and I thought, this is what web development has become? There has to be a better way.
React and Vue.js were marginal improvements, but they didn’t move the needle. There is nothing wrong with JavaScript per se, but why would I want to leave my “Ruby and Rails Happiness Bubble”, as Rahul Subramaniam so eloquently put it.
Why use JavaScript in the first place? Because we want a page event to dynamically update our content without refreshing the entire browser. Well, the basic premise of Hotwire is to accomplish this in Ruby by calling the server for these bits of HTML over the wire (the basis for the name, ‘Hotwire’). It’s JavaScript libraries provide the plumbing, and we continue to code in our Rails happiness bubble.
Sounds awesome, right? 100% agree. Thus, I decided to take Hotwire for a spin having been motivated by a number of talks at RailsConf 2021 this past week.
A Hotwire Installation Surprise
The Hotwire site lists three components that comprise the framework, however Turbo Frames and Turbo Streams are what you will primarily be using. For those occasions when you still need JavaScript, Stimulus will help you do that. Adding this line to your Gemfile gets you these components.
gem 'hotwire-rails'
Strada is the third component, but there is not much information available on it yet. It will support mobile app development.
There are two installation commands to run. First, the standard bundle install and then a specific Hotwire installation.
bin/bundle install
bin/rails hotwire:install
If you are migrating an existing app, at least in my case, I was greeted with an unexpected surprise at this point. Form submissions ceased working immediately after the Hotwise install. I was surprised by this, but discovered that changes made to the default application layout modified it such that requests expected Turbo Streams responses by default.
My application was still returning the same valid HTML as it had before, however there were no Turbo Frames or Streams tags yet. Thus, the client side basically ignored the completely valid content that was being returned to it.
After the initial panic wore off, I settled in and was able to migrate my app without too much trouble.
Hotwire in 30 Seconds
The key concepts to understand with Hotwire are:
- Controller responses are for named segments of the page as identified by Turbo Frame or Stream elements.
- A Turbo Frame is a single section of the page (think Rails-managed iframe) that maintains its own navigation context. By contrast, a Turbo Streams response can include many fragments used to update multiple, arbitrary sections of your page. Thus, the simplest application migration would be to wrap each existing html.erb in a Turbo Frame tag. This is somewhat analogous to what Turbo Drive does, a component I didn’t list earlier because I doubt it will be used for much. It treats your entire page as a logical frame, so while you get rid of the page reload flicker, you are still always regenerating the entire HTML content to send over the wire.
Turbo Frames and Streams are more closely aligned to the AJAX model where you request some data and update relevant page elements. The difference is that in Hotwire your Rails server returns HTML fragments that the library dynamically updates for you, as opposed to having to manage this yourself in JavaScript.
Back in my Happiness Bubble
The phrase “game changer” is wildly overused in this industry. It's amusing to me now how often it shows up needlessly, and thus I find myself on the lookout for its occurrences. But I do actually feel like the Hotwire paradigm is a game changer. Why do I say that?
The ability to write a responsive SPA/PWA all while primarily staying in Ruby/Rails is phenomenal. It is a slight mental shift to go from controller responses as entire pages to fragments, but once you model your actions accordingly, it really is quite easy and fun to create web applications again. Not having to deal with JavaScript frameworks is admittedly my favorite part. But I prefer to look at it the other way. The ability to stay in Ruby while building modern web applications is rewarding.
This feels like a new framework/library/components that will actually improve productivity rather than add to the technology bloat many projects end up becoming. That is a transformative change. Sometimes less is more, and this is a perfect example of that.
Let's Look at Some Actual Code
Here is a screen capture from my Instant Poll application annotated with the named divs that will be updated by Turbo Streams.
This page is initially rendered using the poll partial.
The form submission is handled by the PollController as normal, but instead of a poll/submit.html.erb rendering the page, a new poll/submit,turbo-streams.erb is used, Note that it provides updated HTML for our named divs. It doesn’t just update the div where the form originated, it updates two other divs as well. For this reason, Turbo Streams gives you the most flexibility.
Here is the response from the controller request.
<turbo-stream action="replace" target="poll_title"><template>
You chose <span style="font-weight: bold">Gaming</span>.
Overall results:<br/>
</template></turbo-stream>
<turbo-stream action="replace" target="poll_message">
<template></template>
</turbo-stream>
<turbo-stream action="replace" target="poll_body"><template>
<div class="panel-body">
<ul class="list-group">
<li class="list-group-item">
<div><label>Netflix: 494</label></div>
</li>
<li class="list-group-item">
<div><label>Gaming: 363</label></div>
</li>
<li class="list-group-item">
<div><label>Dining: 517</label></div>
</li>
<li class="list-group-item">
<div><label>NightClub: 800</label></div>
</li>
</ul>
</div>
</template></turbo-stream>
<turbo-stream action="replace" target="poll_footer"><template>
<div class="panel-footer text-center">Thank you for participating!</div>
</template></turbo-stream>
After the dynamic page updates are made thanks to Hotwire libraries, the result is this.
I also added a comments section below the article on this sample app using a Turbo Frame to show the difference between the two approaches. My poll page changes were localized and thus easily could have been implemented using either mechanism. You can imagine banners with shopping cart totals or other information completely separate from the form element being updated with Turbo Streams.
I’ll say it again, and I don’t use the term lightly. Hotwire truly is a game changer, I encourage you to check it out now if you haven’t already,
We will take a look at Stimulus in a future article. Follow me on Twitter at @DarrenBroemmer so you don’t miss any episodes of Ruby Unbundled. Have fun with those Hotwire apps!
Developers, Open Source, App Development, ruby on rails web development
Top comments (0)