At Skroutz we constantly try to find ways to make our website faster and consequently optimize our users’ experience. In this context, Hotwire couldn’t escape our attention as it aroused the interest of the developers community from the moment it was announced by its creators.
What is Hotwire?
Hotwire, as described in the official website, is
The order show page
Let’s get started by setting the context of our example. At Skroutz we have developed a portal, known as Skroutz Merchants, that provides useful tools to our partners in order to facilitate the operation of their store. In one of those views we show the order’s details alongside a list of tickets that may exist and are related to this order.
In order to reduce the initial rendering time, we choose to load the tickets list asynchronously, as soon as the initial render has finished.
The following image illustrates a simplified wireframe of the order show page. The parts that are loaded on the initial render, such as the sidebar, the top bar and the order itself, are colored with green. The tickets list section is colored in orange, indicating that it gets loaded asynchronously, after the initial render.
/merchants/orders/:code/tickets path in order to fetch the tickets, if any.
As shown in the following block,
order_tickets queries the database, checks to see if there are any tickets and creates the HTML from the respective partial template.
Now, let’s see the frontend part.
OrderTicketsView is the class that is responsible for fetching the tickets data and injects the received markup into the DOM. More specifically,
_getOrderTicketsData performs the asynchronous request, finds the
#js-tickets-wrapper element and replaces it with the received markup.
As we can see from
Introducing Turbo Frames
Fortunately, Turbo provides Turbo Frames, a set of techniques that help us decompose a page into independent parts that get updated individually.
Turbo frame is nothing more than a custom HTML element with the
<turbo-frame> tag. Every turbo frame element must have a unique id that is used by Turbo in order to update its contents. Anything that is wrapped within a
<turbo-frame> tag, belongs to a separate context that gets updated independently of the rest of the page.
Lazily loading frames is a special case of turbo frames that fits perfectly to our case. In order to create a lazily loading frame we just have to provide a
src attribute to the
<turbo-frame> element with a url as the value. As soon as the
Applying lazily loading frames
Introducing turbo frames to an existing codebase is quite simple. Just wrap the desired part of the page with a
<turbo-frame> tag and you have created a frame.
In this way, in
show.html.erb view, we replace the
#js-tickets-wrapper div with a
<turbo-frame> tag. The new turbo frame element must have a unique id, so we assign the
order_tickets id, alongside with a url as value of the
src attribute. Finally, we add the
loading: 'lazy' attribute so that the request to the provided url happens only when the turbo frame element becomes visible in the viewport. More details about the available HTML attributes can be found here.
Then, we have to adjust the response of the action that gets called when the turbo frame element requests the provided url. Turbo frame waits for a response that contains HTML markup, so we alter the contents of the
respond_to block in order to return the respective partial view. Furthermore, we no longer need the
options and the
view objects because we don’t build the HTML manually, as we did before with
There is something more that we need to do. We’ll have to adjust the
merchants/tickets/no_tickets_message partial so that it responds with the expected markup.
merchants/tickets/tickets has been created from the beginning in order to wrap the collection of tickets in a way that Turbo can handle.
Turbo frame has to find a way to match the content it receives from the request to the provided url, with the part of the page that it needs to update. As we said earlier, we gave the
order_tickets id to the turbo frame element. Turbo will try to find a
<turbo-frame> tag with the same id inside the response body, and if it finds it, it takes its contents and replace the contents of the
#order_tickets turbo frame element of the page with them.
So, nothing scary, just wrap the contents with a
<turbo-frame> tag with the appropriate id as shown in the following blocks.
order_tickets_view.js, so, we can safely delete it!
In this post, we tried to demonstrate the ease with which we can use Turbo Frames. We have completed the refactoring in three simple steps:
- Wrap the desired part of the page with a
<turbo-frame>tag and give it a unique id and a url as the value of the
- Refactor the controller’s response as needed
- Add the
<turbo-frame>tag with the appropriate id to the partials that get rendered from the controller
Turbo comes with many more techniques, apart from Turbo Frames. Turbo Streams is another powerful feature that can improve the dynamic nature of any app. We can use streams to broadcast changes to our models, from the server to the client. And this is done with a WebSocket connection that Turbo, automatically establishes and handles for us.
In our case, we can take advantage of the power of Turbo Streams and push any updates of a specific order’s tickets to the client, so, users will be able to see a live update (insertion of a new ticket, deletion or edit) on their screen, without having to constantly refresh the page to fetch the latest state.