Contact Us

Turbo cache control in practice

By Stephen, Director of Engineering
15 September 2023
Recently, we had the opportunity to enable Turbo as part of our refresh of the Adelaide Fringe website for the 2024 season and we're really happy with the outcome. As Rails developers, we're always on the lookout for ways to improve the performance and user experience of our applications.

Turbo is a javascript framework from the Rails team that replaces browser navigation between pages with an event model that gives a app-like experience by keeping scripts and styles in memory and only reloading the page body.

In this article, we want to share our experiences using the turbo cache control attributes, "turbo-permanent" and "turbo-temporary", to manage shopping cart state.

Understanding turbo permanent

Before diving into implementation details, let's take a moment to understand what Turbo Permanent attribute does and why it matters.

When using Turbo, your application's pages are cached to improve navigation speed. This cache retains the full serialised DOM from previous renders, so that when the user returns to a page they have previously visited, Turbo can re-vivify and render the cache while it waits for data from the server. The Turbo Permanent attribute is intended to allow programmers to prevent an element on the page from being replaced during a turbo visit.

For example, on the Adelaide Fringe website, we show a cart icon that indicates how many items the user has in their shopping cart.
Screenshot of the Adelaide Fringe website showing one item in the cart
An example of the cart icon showing one item

The cart contents count conundrum

Consider a common scenario: a user adds an item to their cart. You perform the update of the server-side cart state and give the user context by updating the cart’s item count and rendering it in the top right corner of the screen. However, what happens if the user decides to go back to a previous page? You don’t want the cart count to show the previous value during the turbo restoration visit; it should remain the same.

Turbo permanent to the rescue

This is where Turbo Permanent comes into play. It allows you to instruct Turbo not to re-render an element but to retain the current DOM element from the previous page. However, it’s not as simple as slapping a `turbo:permanent` attribute on an element.
<div id="cart-count" data-turbo-permanent>1</div>
When an element is marked as turbo permanent, turbo will look for an element on the new page with the same ID, and as long as they are both marked permanent, then Turbo will keep the DOM element from the previous render in place of the 'new' element. This works for cached elements and for new elements from the server.

This poses a challenge when you need to update that element from the server. To address this, we turned to a trusty Stimulus controller to dynamically append the turbo permanent attribute to the element on Stimulus connect. By doing this, we ensured that if the server sends data for this attribute, it will replace the data in the page. But, when the element is being restored from the Turbo cache, it will be ignored, allowing us to maintain that precious cart state.

Can we do better?

As an enhancement to this setup, we went a step further. We decided to suppress rendering the cart element from the server layout unless the cart had been updated.

This approach not only enhances performance by avoiding interacting with the database cart model for non-cart-related requests, but also allows us to cache the rendered page on the server-side for high-traffic pages.

Implementation details

Here’s how we implemented this in our Rails application:

1. We start by rendering an asynchronous turbo frame for the cart. The frame is marked turbo-permanent so that it will only load data from the server on first render.
2. Turbo will automatically load the URL specified in the frame on page load.
3. We respond to the request with a non-permanent turbo frame – this is important, as we want turbo to render the response.
4. The turbo frame includes a Stimulus controller that adds the turbo permanent attribute on connect. This ensures that it will not be replaced on Turbo visits.

When the cart changes

We use turbo streams to push changes to the browser when the cart changes. These streams render the same frame component without turbo permanent to ensure the change will be rendered into the DOM. Once again, we use the Stimulus controller to add the turbo permanent attribute on connect to "freeze" the content and ensure it will be retained until the next stream update.

Wrapping up

Incorporating turbo permanent attributes into your application can create the impression of a fully-managed single page application within a simple and easy to manage Rails MVC application. By carefully orchestrating these attributes with Stimulus controllers, you can ensure a seamless user experience, improved performance, and efficient server-side caching.

As a team we love the challenge of working on lots of different customer application domains and appreciate the trust and support we get from our clients to integrate new technologies and build our expertise.

We're excited for the Adelaide Fringe 2024 season. Event registrations are already open. Hope to see you there!