Introduction to Turbo: Speed up your Rails Apps with Hotwire

Reading Time: 5 minutes

What is Turbo?

Turbo is a technology developed by the creators of the Ruby on Rails framework whose intent is to help developers build web applications with the speed of a single page application using a minimal amount of Javascript code.

Turbo belongs to a bundle of server-side frameworks and technologies called Hotwire. Turbo is complementary to the other technologies that compose Hotwire and together can be used to create a responsive user experience while keeping most of the logic on the server side.

Installation

Starting from Rails version 7, Turbo is configured by default unless the --skip-hotwire flag is passed to the rails new generator. For Rails apps with versions previous to 7, you need to follow the following steps:

  • Add the turbo-rails gem to your Gemfile: gem ‘turbo-rails’
  • Run ./bin/bundle install
  • Run ./bin/rails turbo:install
  • Run ./bin/rails turbo:install:redis to change the development Action Cable adapter from Async (the default one) to Redis. The Async adapter does not support Turbo Stream broadcasting.

How does it work?

When a page is reloaded the browser re-downloads and re-initializes the CSS and Javascript which is a time-consuming operation. In order to avoid that, Turbo applies some strategies to enable partial page updates and leverage AJAX. Turbo comprises three main components: Turbo Drive, Turbo Frames, and Turbo Streams. Each plays a unique role in enhancing web application performance and interactivity.

Turbo Drive

Turbo Drive, the successor to the Turbolinks framework, intercepts all clicks on links to the same domain. Instead of following the link, it uses the History API to change the URL, fetches the new page, and replaces the current body with the new content.

The difference with the Turbolinks framework is that Turbo Drive can also handle form submissions and responses. It handles form submissions in a manner similar to link clicks. The key difference is that form submissions can issue stateful requests using the HTTP POST method, while link clicks only ever issue stateless HTTP GET requests.

Disabling Turbo Drive

You can disable Turbo Drive for a specific link if required with the data-turbo=”false” tag:

<a class="nav-link" data-turbo="false" href="/">Home</a>

Also, you can set turbo drive to be disabled by default with the following configuration:

import "@hotwired/turbo-rails"
Turbo.session.drive = false

The you can enable it for specific links as follows:

<a class="nav-link" data-turbo="true" href="/">Home</a>

Turbo drive automatically loads links on “mouseenter” which increases the perceived speed of the interaction. That means that when the user clicks on the link, the page is already loaded so only the content is swapped.

You can disable prefetching with the data-turbo-prefetch="false" tag:

<a href="/about" data-turbo-prefetch="false">About</a>

Or disable prefetching altogether with the following meta tag:

<meta name="turbo-prefetch" content="false">

Turbo Frames

Turbo Frames allow dividing a web application into independent components. For instance, you can edit a blog post directly on the same page, replacing only the relevant part of the content without a full page reload.

Frames are created by wrapping a segment of the page in a <turbo-frame> element which should have a unique ID. That ID will be used to match the content being replaced when requesting new pages from the server.

For instance, let’s suppose we want to edit a blog Post from the same page where we are showing its content without having to navigate to another URL. We could define a frame that replaces the current content of the blog Post with the form we use for edition.

The first step would be to define the area we want to replace in the app/views/posts/show.html.erb view. We can use the turbo_frame_tag helper to create the frame. In this case we gave the frame the ID “post”:

​​<h1>Post</h1>

<%= turbo_frame_tag "post" do %>
  <%= render @post %>

  <div>
    <%= link_to "Edit this post", edit_post_path(@post) %> |
    <%= link_to "Back to posts", posts_path %>

    <%= button_to "Destroy this post", @post, method: :delete %>
  </div>
<% end %>

Now, after we click the “Edit this post” link, the PostsController will render the app/views/posts/edit.html.erb view. So, we open that view and add a frame with the same “post” ID we used in the first frame:

<h1>Editing post</h1>

<%= turbo_frame_tag "post" do %>
  <%= render "form", post: @post %>

  <br>

  <div>
    <%= link_to "Show this post", @post %> |
    <%= link_to "Back to posts", posts_path %>
  </div>
<% end %>

Notice that the <h1>Editing post</h1> part is outside of the frame, which means it won’t be rendered.

Now, the result of clicking on the Edit this post link will be that the content of the post will be replaced by the Post’s edit form. Notice how the The <h1>Post</h1> that is part of the app/views/posts/show.html.erb view never gets replaced as it is outside of the frame.

Initial view:

View with replaced content:

Turbo Streams

Turbo Streams enable asynchronous partial page updates via a web socket connection. This is useful for real-time updates, such as adding new posts to a list without refreshing the page.

For instance, following the Blog example mentioned above, let’s suppose we need to add new Posts from the index view and then see the new Post added to the list of Posts.

The first step would be to add the Posts’s form to the app/views/posts/index.html.erb view. Notice that the div element surrounding the list of Posts has the id “posts”:

<h1>Posts</h1>

<div id="posts">
  <%= render partial: "posts/form", locals: {post: Post.new} %>
  <% @posts.each do |post| %>
    <%= render post %>
  <% end %>
</div>

<%= link_to "New post", new_post_path %>

Then we tell the the PostsController to generate a turbo-stream response passing “posts” as the id of the element that will be the target of the “append” action:

def create
    @post = Post.create!(post_params)

    respond_to do |format|
      format.turbo_stream do
        render turbo_stream: turbo_stream.append(:posts, partial: 'posts/post', locals: { post: @post })
      end

      format.html { redirect_to posts_url }
    end
  end

And that’s it. Now, after creating a new Post using the form we added in the index view, it will be automatically added to the list of posts.

TL;DR

Turbo, part of the Hotwire suite, helps build fast and responsive web applications using minimal JavaScript. It uses three main components:

  • Turbo Drive: Intercepts clicks and form submissions, updating the page using AJAX.
  • Turbo Frames: Divides the page into components that can be dynamically updated.
  • Turbo Streams: Allows asynchronous updates over a web socket connection.

Hire an Expert

Building a fast and responsive web application can be challenging, but we got you covered!. Our skilled team of developers excels in harnessing advanced technologies like Turbo to deliver seamless and high-performance web applications. Whether you’re embarking on a new project or enhancing an existing one, we’re here to help you achieve your goals with ease and expertise.

Hire an expert from MagmaLabs today and take your web development to the next level!

0 Shares:
You May Also Like

Sprockets + Sinatra

Reading Time: 2 minutesIntegrating Sprockets and Sinatra is easy because Sprockets is Rack-based. Let's do a simple example focusing on: Creating…