DEV Community

matt swanson
matt swanson

Posted on • Originally published at on

Lazy-loading content with Turbo Frames and skeleton loader

Hotwire is a new suite of frontend tools from Basecamp for building “reactive Rails” apps while writing a minimal amount of JavaScript.

While the most exciting feature to some is the real-time streaming of server rendered HTML, my favorite addition is the Turbo Frame.

The Turbo Frame is a super-charged iFrame that doesn’t make you cringe when you use it. Frames represent a slice of your page and have their own navigation context.

One incredibly powerful feature is lazy-loading a Frame. One example of this pattern that you probably see everyday is the GitHub activity feed:

GitHub Activity Feed: Lazy load

First you load the “outer shell” of the page and then you can make an AJAX call to fetch more content to fill in the rest of the page. It’s a great way to speed up a slow page.

But one downside is that the page content jumps around a bit. The “Loading” spinner is one small rectangle, but the result is a long feed of events.

A way to solve this problem is to use a “skeleton screen” or “skeleton loader”. This UI pattern uses a blank version of the content as a placeholder and reduces the jarring impact when the content finally loads.

Skeleton loader

These two concepts go together like peanut butter and jelly.


A basic lazy-loaded Turbo Frame looks like this:

<turbo-frame id="feed" src="/feed">
  Content will be replaced when /feed has been loaded.
Enter fullscreen mode Exit fullscreen mode

By specifying the src attribute, the Frame will automatically make an AJAX request when the page loads and replace its content with the matching <turbo-frame> in the response.

Additionally, you can set the loading property of the loading to be either “eager” (load right away) or “lazy” (load once the frame is visible on the page).

Here’s how the GitHub Activity feed might look in a Rails view:

<%= turbo_frame_tag :feed, src: activity_feed_path, loading: :lazy do %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

You can take it to the next level by replacing the basic “Loading…” message with your own skeleton loader. Tailwind makes this really easy with the built-in animate-pulse class.

Simply add some gray rectangles as your initial frame contents:

<%= turbo_frame_tag :feed, src: activity_feed_path, loading: :lazy do %>
  <div class="flex flex-col space-y-6">
    <% 10.times do %>
      <div class="animate-pulse flex space-x-4">
        <!-- Avatar -->
        <div class="rounded-full bg-gray-400 h-12 w-12"></div>

        <!-- Details -->
        <div class="flex-1 space-y-4 py-1">
          <div class="h-4 bg-gray-400 rounded w-3/4"></div>
          <div class="space-y-2">
            <div class="h-4 bg-gray-400 rounded"></div>
            <div class="h-4 bg-gray-400 rounded w-5/6"></div>
    <% end %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

Once you wrap your head around the power of Turbo Frames, soon you’ll be spotting all kinds of places in your app that can benefit from lazy-loading some good old HTML.

Additional Resources

Hotwire Docs: Turbo Frames

Tailwind Docs: animate-pulse

Top comments (0)