Skip to content

Blogging On Rails

Everything on Rails!

Adding Loading Screen with Turbo

One great aspect of Turbo frames is lazy loading. You can use this behavior to quickly load in the shell of a UI, and then lazy load all the data. Adding an animating loading status will help give the feeling of immediacy and “a lot of work is happening in the background” without the frustration that the page is stuck. We don’t even need any extra Javascript, since we can animate the page with CSS.

Loading many books

This tutorial will start from a previous example, Modeling More Complex Datasets, which used a Book model. We’ll start from scratch with a fresh Rails app and use TailwindCSS for the style.

$ rails new BookData -c tailwind
$ cd BookData 
$ rails g model Book title:string author:string publisher:string category:string isbn:string dewey_decimal_number:string binding:integer
$ rails db:migrate

We’re going to make binding an enum since there are only a few options, so change models/book.rb to this:

class Book < ApplicationRecord
  enum binding: [:hardcover, :paperback]
end

To test our system with plenty of books, We will use the Faker gem to create about 2000 fake books.

In our Gemfile:

gem 'faker'

Then run

$ bundle install

Then we’ll create the books in our db/seeds.rb file.

2_000.times do |i|
  Book.create!( title: Faker::Book.title, 
	author: Faker::Book.author, 
	publisher: Faker::Book.publisher,
	category: Faker::Book.genre,
	isbn: "#{Faker::Number.number(digits: 3)}-#{Faker::Number.number(digits: 1)}-#{Faker::Number.number(digits: 2)}-#{Faker::Number.number(digits: 6)}-#{Faker::Number.number(digits: 1)}",
	dewey_decimal_number: "#{Faker::Number.number(digits: 3)}.#{Faker::Number.number(digits: 3)}",
	binding: Faker::Number.between(from: 0, to: 1))
  print '.' if i % 100 == 0
end

Now we have a lot of books records that we can use.

Let’s build an index page so that we can look at our books.

$ rails g controller books index

This will update routes.rb:

Rails.application.routes.draw do
  get 'books/index'
end

And we need to update app/controllers/books_controller.rb file with an index action, and we’ll start by loading all the books.

class BooksController < ApplicationController
  def index
    @books = Book.all
  end
end

And finally, a view at app/views/books/index.html.erb.

<div>
  <h1 class="font-bold text-4xl">All Books</h1>
  <table>
    <thead>
      <tr>
        <th>Title</th>
        <th>Author</th>
        <th>Publisher</th>
        <th>Category</th>
      </tr>
    </thead>
    <tbody>
      <% @books.each do |book| %>
        <tr>
          <td><%= book.title %></td>
          <td><%= book.author %></td>
          <td><%= book.publisher %></td>
          <td><%= book.category %></td>
        </tr>
      <% end %>
    </tbody>
  </table>
</div>

Run ./bin/dev and visit it in the browser.

Lazy Loading

The URL path /books/index is a little awkward. It would be great if it would load when the site loads up. Let’s add a root controller that will load the books page:

$ rails g controller root index

Change the routes.rb file to load at the root page:

Rails.application.routes.draw do
  get 'books/index'
  root "root#index"
end

Add a turbo-frame element to the root/index.html.erb page:

<div>
  <h1 class="font-bold text-4xl">Root Page</h1>
  <%= turbo_frame_tag "books", src: books_index_path %>
</div>

If you don’t change the book’s index page, this will load all the books in, and overwrite the URL. We want to hide the /books/index path, so let’s wrap the table in views/books/index.html.erb in a turbo_frame_tag:

<div>
  <h1 class="font-bold text-4xl">All Books</h1>
  <%= turbo_frame_tag "books" do %>
    <table>
      <thead>
        <tr>
          <th>Title</th>
          <th>Author</th>
          <th>Publisher</th>
          <th>Category</th>
        </tr>
      </thead>
      <tbody>
        <% @books.each do |book| %>
          <tr>
            <td><%= book.title %></td>
            <td><%= book.author %></td>
            <td><%= book.publisher %></td>
            <td><%= book.category %></td>
          </tr>
        <% end %>
      </tbody>
    </table>
  <% end %>
</div>

Now, when you load the root page, there is a skeleton that loads in the books table.

Animate the loading

Loading all these books could take a while. This could stand in for a different page with more complicated database calls that could take a few seconds. Let’s put in a small skeleton, animate a shimmering effect, and give the appearance that the page is working in the background. Look at the revised /views/root/index.html.erb:

<div>
  <h1 class="font-bold text-4xl">Root Page</h1>
  <%= turbo_frame_tag "books", src: books_index_path do %>
    <table class="animate-pulse">
      <thead>
        <tr>
          <th>Title</th>
          <th>Author</th>
          <th>Publisher</th>
          <th>Category</th>
        </tr>
      </thead>
      <tbody>
        <% (1..10).each do %>
          <tr>
            <td><div class="bg-slate-200 h-8 rounded w-64"></div></td>
            <td><div class="bg-slate-200 h-8 rounded w-64"></div></td>
            <td><div class="bg-slate-200 h-8 rounded w-64"></div></td>
            <td><div class="bg-slate-200 h-8 rounded w-64"></div></td>
          </tr>
        <% end %>
      </tbody>
    </table>
  <% end %>
</div>

The turbo-frame now has a block that puts in a skeleton table. It uses the tailwind animate-pulse class, which is a pulsing CSS animation. Instead of iterating through book records, it generates 10 rows. Each td cell has a rounded div that acts a visual placeholder. Loading the page shows a pleasant placeholder while all the books come in over the wire.

Free Interactivity

This shows how we can use a lazy loading page to add interactivity to our page with little extra work, and no extra JavaScript. You could imagine having to see up a Stimulus controller that loaded data in when it connected. Turbo allows us to skip even that step, and just use basic HTML and an extra Rails controller. You could even build a dashboard that loaded data from several sources.

Looking to make your Rails app more interactive?

Subscribe below, and you won’t miss the next Rails tutorial, that will teach you how to make really interactive web apps that just work.

We won’t send you spam. Unsubscribe at any time.

One comment on “Adding Loading Screen with Turbo”

Leave a Reply

Your email address will not be published. Required fields are marked *